diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + return true; +} + +} // end of namespace iristrt diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); + +signals: + +}; + +#endif // IRISDATADAO_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + QString save(QVariantMap object); + bool edit(QVariantMap newObject, QString id); + bool dele(QString id); +signals: + +}; + +#endif // SYSPERSONDAO_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..8bd1b8c --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,37 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + void closeConnect(); + QByteArray getResponse(); + void resetRecvBuffer(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + void socketReconnect(); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..8bd1b8c --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,37 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + void closeConnect(); + QByteArray getResponse(); + void resetRecvBuffer(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + void socketReconnect(); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..933809c --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,35 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); + + // 循环播放 + effect.setLoopCount(QSoundEffect::Null); + // 设置音量,0-1 + effect.setVolume(1.0f); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} + +void SpeakerUtil::sayIdentifySuccessZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recSuccess.wav")); + effect.play(); +} + +void SpeakerUtil::sayIdentifyFailureZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recFailureRetry.wav")); + effect.play(); +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..8bd1b8c --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,37 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + void closeConnect(); + QByteArray getResponse(); + void resetRecvBuffer(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + void socketReconnect(); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..933809c --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,35 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); + + // 循环播放 + effect.setLoopCount(QSoundEffect::Null); + // 设置音量,0-1 + effect.setVolume(1.0f); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} + +void SpeakerUtil::sayIdentifySuccessZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recSuccess.wav")); + effect.play(); +} + +void SpeakerUtil::sayIdentifyFailureZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recFailureRetry.wav")); + effect.play(); +} diff --git a/utils/SpeakerUtil.h b/utils/SpeakerUtil.h new file mode 100644 index 0000000..02b221b --- /dev/null +++ b/utils/SpeakerUtil.h @@ -0,0 +1,35 @@ +#ifndef SPEAKERUTIL_H +#define SPEAKERUTIL_H + +#include +#include +#include + +class SpeakerUtil : public QObject +{ + Q_OBJECT +public: + ~SpeakerUtil() {} + SpeakerUtil(const SpeakerUtil&)=delete; + SpeakerUtil& operator=(const SpeakerUtil&)=delete; + + static SpeakerUtil& getInstance() { + static SpeakerUtil instance; + return instance; + } + + void speak(QString content); + void sayIdentifySuccessZhCn(); + void sayIdentifyFailureZhCn(); + +private: + explicit SpeakerUtil(QObject *parent = nullptr); + + QTextToSpeech * tts; + QSoundEffect effect; + +signals: + +}; + +#endif // SPEAKERUTIL_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..8bd1b8c --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,37 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + void closeConnect(); + QByteArray getResponse(); + void resetRecvBuffer(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + void socketReconnect(); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..933809c --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,35 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); + + // 循环播放 + effect.setLoopCount(QSoundEffect::Null); + // 设置音量,0-1 + effect.setVolume(1.0f); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} + +void SpeakerUtil::sayIdentifySuccessZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recSuccess.wav")); + effect.play(); +} + +void SpeakerUtil::sayIdentifyFailureZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recFailureRetry.wav")); + effect.play(); +} diff --git a/utils/SpeakerUtil.h b/utils/SpeakerUtil.h new file mode 100644 index 0000000..02b221b --- /dev/null +++ b/utils/SpeakerUtil.h @@ -0,0 +1,35 @@ +#ifndef SPEAKERUTIL_H +#define SPEAKERUTIL_H + +#include +#include +#include + +class SpeakerUtil : public QObject +{ + Q_OBJECT +public: + ~SpeakerUtil() {} + SpeakerUtil(const SpeakerUtil&)=delete; + SpeakerUtil& operator=(const SpeakerUtil&)=delete; + + static SpeakerUtil& getInstance() { + static SpeakerUtil instance; + return instance; + } + + void speak(QString content); + void sayIdentifySuccessZhCn(); + void sayIdentifyFailureZhCn(); + +private: + explicit SpeakerUtil(QObject *parent = nullptr); + + QTextToSpeech * tts; + QSoundEffect effect; + +signals: + +}; + +#endif // SPEAKERUTIL_H diff --git a/utils/TimeCounterUtil.cpp b/utils/TimeCounterUtil.cpp new file mode 100644 index 0000000..78c3d69 --- /dev/null +++ b/utils/TimeCounterUtil.cpp @@ -0,0 +1,9 @@ +#include "TimeCounterUtil.h" + +TimeCounterUtil::TimeCounterUtil() +{ + faceCapCounter = new QTimer(); + irisCapCounter = new QTimer(); + clockCounter = new QTimer(); + videoCounter = new QTimer(); +} diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..8bd1b8c --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,37 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + void closeConnect(); + QByteArray getResponse(); + void resetRecvBuffer(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + void socketReconnect(); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..933809c --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,35 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); + + // 循环播放 + effect.setLoopCount(QSoundEffect::Null); + // 设置音量,0-1 + effect.setVolume(1.0f); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} + +void SpeakerUtil::sayIdentifySuccessZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recSuccess.wav")); + effect.play(); +} + +void SpeakerUtil::sayIdentifyFailureZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recFailureRetry.wav")); + effect.play(); +} diff --git a/utils/SpeakerUtil.h b/utils/SpeakerUtil.h new file mode 100644 index 0000000..02b221b --- /dev/null +++ b/utils/SpeakerUtil.h @@ -0,0 +1,35 @@ +#ifndef SPEAKERUTIL_H +#define SPEAKERUTIL_H + +#include +#include +#include + +class SpeakerUtil : public QObject +{ + Q_OBJECT +public: + ~SpeakerUtil() {} + SpeakerUtil(const SpeakerUtil&)=delete; + SpeakerUtil& operator=(const SpeakerUtil&)=delete; + + static SpeakerUtil& getInstance() { + static SpeakerUtil instance; + return instance; + } + + void speak(QString content); + void sayIdentifySuccessZhCn(); + void sayIdentifyFailureZhCn(); + +private: + explicit SpeakerUtil(QObject *parent = nullptr); + + QTextToSpeech * tts; + QSoundEffect effect; + +signals: + +}; + +#endif // SPEAKERUTIL_H diff --git a/utils/TimeCounterUtil.cpp b/utils/TimeCounterUtil.cpp new file mode 100644 index 0000000..78c3d69 --- /dev/null +++ b/utils/TimeCounterUtil.cpp @@ -0,0 +1,9 @@ +#include "TimeCounterUtil.h" + +TimeCounterUtil::TimeCounterUtil() +{ + faceCapCounter = new QTimer(); + irisCapCounter = new QTimer(); + clockCounter = new QTimer(); + videoCounter = new QTimer(); +} diff --git a/utils/TimeCounterUtil.h b/utils/TimeCounterUtil.h new file mode 100644 index 0000000..91f8da0 --- /dev/null +++ b/utils/TimeCounterUtil.h @@ -0,0 +1,30 @@ +#ifndef TIMECOUNTERUTIL_H +#define TIMECOUNTERUTIL_H + +#include +#include + +class TimeCounterUtil : public QObject +{ +public: + + ~TimeCounterUtil() {}; + TimeCounterUtil(const TimeCounterUtil&)=delete; + TimeCounterUtil& operator=(const TimeCounterUtil&)=delete; + + static TimeCounterUtil& getInstance() { + static TimeCounterUtil instance; + return instance; + } + + QTimer * faceCapCounter; + QTimer * irisCapCounter; + QTimer * clockCounter; + QTimer * videoCounter; // show video images on main window + +private: + TimeCounterUtil(); + +}; + +#endif // TIMECOUNTERUTIL_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..8bd1b8c --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,37 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + void closeConnect(); + QByteArray getResponse(); + void resetRecvBuffer(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + void socketReconnect(); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..933809c --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,35 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); + + // 循环播放 + effect.setLoopCount(QSoundEffect::Null); + // 设置音量,0-1 + effect.setVolume(1.0f); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} + +void SpeakerUtil::sayIdentifySuccessZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recSuccess.wav")); + effect.play(); +} + +void SpeakerUtil::sayIdentifyFailureZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recFailureRetry.wav")); + effect.play(); +} diff --git a/utils/SpeakerUtil.h b/utils/SpeakerUtil.h new file mode 100644 index 0000000..02b221b --- /dev/null +++ b/utils/SpeakerUtil.h @@ -0,0 +1,35 @@ +#ifndef SPEAKERUTIL_H +#define SPEAKERUTIL_H + +#include +#include +#include + +class SpeakerUtil : public QObject +{ + Q_OBJECT +public: + ~SpeakerUtil() {} + SpeakerUtil(const SpeakerUtil&)=delete; + SpeakerUtil& operator=(const SpeakerUtil&)=delete; + + static SpeakerUtil& getInstance() { + static SpeakerUtil instance; + return instance; + } + + void speak(QString content); + void sayIdentifySuccessZhCn(); + void sayIdentifyFailureZhCn(); + +private: + explicit SpeakerUtil(QObject *parent = nullptr); + + QTextToSpeech * tts; + QSoundEffect effect; + +signals: + +}; + +#endif // SPEAKERUTIL_H diff --git a/utils/TimeCounterUtil.cpp b/utils/TimeCounterUtil.cpp new file mode 100644 index 0000000..78c3d69 --- /dev/null +++ b/utils/TimeCounterUtil.cpp @@ -0,0 +1,9 @@ +#include "TimeCounterUtil.h" + +TimeCounterUtil::TimeCounterUtil() +{ + faceCapCounter = new QTimer(); + irisCapCounter = new QTimer(); + clockCounter = new QTimer(); + videoCounter = new QTimer(); +} diff --git a/utils/TimeCounterUtil.h b/utils/TimeCounterUtil.h new file mode 100644 index 0000000..91f8da0 --- /dev/null +++ b/utils/TimeCounterUtil.h @@ -0,0 +1,30 @@ +#ifndef TIMECOUNTERUTIL_H +#define TIMECOUNTERUTIL_H + +#include +#include + +class TimeCounterUtil : public QObject +{ +public: + + ~TimeCounterUtil() {}; + TimeCounterUtil(const TimeCounterUtil&)=delete; + TimeCounterUtil& operator=(const TimeCounterUtil&)=delete; + + static TimeCounterUtil& getInstance() { + static TimeCounterUtil instance; + return instance; + } + + QTimer * faceCapCounter; + QTimer * irisCapCounter; + QTimer * clockCounter; + QTimer * videoCounter; // show video images on main window + +private: + TimeCounterUtil(); + +}; + +#endif // TIMECOUNTERUTIL_H diff --git a/utils/UtilInclude.h b/utils/UtilInclude.h new file mode 100644 index 0000000..bea17be --- /dev/null +++ b/utils/UtilInclude.h @@ -0,0 +1,15 @@ +#ifndef UTILINCLUDE_H +#define UTILINCLUDE_H + +#include +#include "ByteUtil.h" +#include "ImageUtil.h" +#include "LogUtil.h" +#include "SettingConfig.h" +#include "SocketClientUtil.h" +#include "SpeakerUtil.h" +#include "TimeCounterUtil.h" +//#include "UDPClientUtil.h" +//#include "HttpRequestUtil.h" + +#endif // UTILINCLUDE_H diff --git a/.gitignore b/.gitignore index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..8bd1b8c --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,37 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + void closeConnect(); + QByteArray getResponse(); + void resetRecvBuffer(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + void socketReconnect(); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..933809c --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,35 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); + + // 循环播放 + effect.setLoopCount(QSoundEffect::Null); + // 设置音量,0-1 + effect.setVolume(1.0f); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} + +void SpeakerUtil::sayIdentifySuccessZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recSuccess.wav")); + effect.play(); +} + +void SpeakerUtil::sayIdentifyFailureZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recFailureRetry.wav")); + effect.play(); +} diff --git a/utils/SpeakerUtil.h b/utils/SpeakerUtil.h new file mode 100644 index 0000000..02b221b --- /dev/null +++ b/utils/SpeakerUtil.h @@ -0,0 +1,35 @@ +#ifndef SPEAKERUTIL_H +#define SPEAKERUTIL_H + +#include +#include +#include + +class SpeakerUtil : public QObject +{ + Q_OBJECT +public: + ~SpeakerUtil() {} + SpeakerUtil(const SpeakerUtil&)=delete; + SpeakerUtil& operator=(const SpeakerUtil&)=delete; + + static SpeakerUtil& getInstance() { + static SpeakerUtil instance; + return instance; + } + + void speak(QString content); + void sayIdentifySuccessZhCn(); + void sayIdentifyFailureZhCn(); + +private: + explicit SpeakerUtil(QObject *parent = nullptr); + + QTextToSpeech * tts; + QSoundEffect effect; + +signals: + +}; + +#endif // SPEAKERUTIL_H diff --git a/utils/TimeCounterUtil.cpp b/utils/TimeCounterUtil.cpp new file mode 100644 index 0000000..78c3d69 --- /dev/null +++ b/utils/TimeCounterUtil.cpp @@ -0,0 +1,9 @@ +#include "TimeCounterUtil.h" + +TimeCounterUtil::TimeCounterUtil() +{ + faceCapCounter = new QTimer(); + irisCapCounter = new QTimer(); + clockCounter = new QTimer(); + videoCounter = new QTimer(); +} diff --git a/utils/TimeCounterUtil.h b/utils/TimeCounterUtil.h new file mode 100644 index 0000000..91f8da0 --- /dev/null +++ b/utils/TimeCounterUtil.h @@ -0,0 +1,30 @@ +#ifndef TIMECOUNTERUTIL_H +#define TIMECOUNTERUTIL_H + +#include +#include + +class TimeCounterUtil : public QObject +{ +public: + + ~TimeCounterUtil() {}; + TimeCounterUtil(const TimeCounterUtil&)=delete; + TimeCounterUtil& operator=(const TimeCounterUtil&)=delete; + + static TimeCounterUtil& getInstance() { + static TimeCounterUtil instance; + return instance; + } + + QTimer * faceCapCounter; + QTimer * irisCapCounter; + QTimer * clockCounter; + QTimer * videoCounter; // show video images on main window + +private: + TimeCounterUtil(); + +}; + +#endif // TIMECOUNTERUTIL_H diff --git a/utils/UtilInclude.h b/utils/UtilInclude.h new file mode 100644 index 0000000..bea17be --- /dev/null +++ b/utils/UtilInclude.h @@ -0,0 +1,15 @@ +#ifndef UTILINCLUDE_H +#define UTILINCLUDE_H + +#include +#include "ByteUtil.h" +#include "ImageUtil.h" +#include "LogUtil.h" +#include "SettingConfig.h" +#include "SocketClientUtil.h" +#include "SpeakerUtil.h" +#include "TimeCounterUtil.h" +//#include "UDPClientUtil.h" +//#include "HttpRequestUtil.h" + +#endif // UTILINCLUDE_H diff --git a/utils/id/IdWorker.h b/utils/id/IdWorker.h new file mode 100644 index 0000000..079bbcb --- /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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..8bd1b8c --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,37 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + void closeConnect(); + QByteArray getResponse(); + void resetRecvBuffer(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + void socketReconnect(); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..933809c --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,35 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); + + // 循环播放 + effect.setLoopCount(QSoundEffect::Null); + // 设置音量,0-1 + effect.setVolume(1.0f); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} + +void SpeakerUtil::sayIdentifySuccessZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recSuccess.wav")); + effect.play(); +} + +void SpeakerUtil::sayIdentifyFailureZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recFailureRetry.wav")); + effect.play(); +} diff --git a/utils/SpeakerUtil.h b/utils/SpeakerUtil.h new file mode 100644 index 0000000..02b221b --- /dev/null +++ b/utils/SpeakerUtil.h @@ -0,0 +1,35 @@ +#ifndef SPEAKERUTIL_H +#define SPEAKERUTIL_H + +#include +#include +#include + +class SpeakerUtil : public QObject +{ + Q_OBJECT +public: + ~SpeakerUtil() {} + SpeakerUtil(const SpeakerUtil&)=delete; + SpeakerUtil& operator=(const SpeakerUtil&)=delete; + + static SpeakerUtil& getInstance() { + static SpeakerUtil instance; + return instance; + } + + void speak(QString content); + void sayIdentifySuccessZhCn(); + void sayIdentifyFailureZhCn(); + +private: + explicit SpeakerUtil(QObject *parent = nullptr); + + QTextToSpeech * tts; + QSoundEffect effect; + +signals: + +}; + +#endif // SPEAKERUTIL_H diff --git a/utils/TimeCounterUtil.cpp b/utils/TimeCounterUtil.cpp new file mode 100644 index 0000000..78c3d69 --- /dev/null +++ b/utils/TimeCounterUtil.cpp @@ -0,0 +1,9 @@ +#include "TimeCounterUtil.h" + +TimeCounterUtil::TimeCounterUtil() +{ + faceCapCounter = new QTimer(); + irisCapCounter = new QTimer(); + clockCounter = new QTimer(); + videoCounter = new QTimer(); +} diff --git a/utils/TimeCounterUtil.h b/utils/TimeCounterUtil.h new file mode 100644 index 0000000..91f8da0 --- /dev/null +++ b/utils/TimeCounterUtil.h @@ -0,0 +1,30 @@ +#ifndef TIMECOUNTERUTIL_H +#define TIMECOUNTERUTIL_H + +#include +#include + +class TimeCounterUtil : public QObject +{ +public: + + ~TimeCounterUtil() {}; + TimeCounterUtil(const TimeCounterUtil&)=delete; + TimeCounterUtil& operator=(const TimeCounterUtil&)=delete; + + static TimeCounterUtil& getInstance() { + static TimeCounterUtil instance; + return instance; + } + + QTimer * faceCapCounter; + QTimer * irisCapCounter; + QTimer * clockCounter; + QTimer * videoCounter; // show video images on main window + +private: + TimeCounterUtil(); + +}; + +#endif // TIMECOUNTERUTIL_H diff --git a/utils/UtilInclude.h b/utils/UtilInclude.h new file mode 100644 index 0000000..bea17be --- /dev/null +++ b/utils/UtilInclude.h @@ -0,0 +1,15 @@ +#ifndef UTILINCLUDE_H +#define UTILINCLUDE_H + +#include +#include "ByteUtil.h" +#include "ImageUtil.h" +#include "LogUtil.h" +#include "SettingConfig.h" +#include "SocketClientUtil.h" +#include "SpeakerUtil.h" +#include "TimeCounterUtil.h" +//#include "UDPClientUtil.h" +//#include "HttpRequestUtil.h" + +#endif // UTILINCLUDE_H diff --git a/utils/id/IdWorker.h b/utils/id/IdWorker.h new file mode 100644 index 0000000..079bbcb --- /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..4fc555a --- /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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..8bd1b8c --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,37 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + void closeConnect(); + QByteArray getResponse(); + void resetRecvBuffer(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + void socketReconnect(); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..933809c --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,35 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); + + // 循环播放 + effect.setLoopCount(QSoundEffect::Null); + // 设置音量,0-1 + effect.setVolume(1.0f); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} + +void SpeakerUtil::sayIdentifySuccessZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recSuccess.wav")); + effect.play(); +} + +void SpeakerUtil::sayIdentifyFailureZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recFailureRetry.wav")); + effect.play(); +} diff --git a/utils/SpeakerUtil.h b/utils/SpeakerUtil.h new file mode 100644 index 0000000..02b221b --- /dev/null +++ b/utils/SpeakerUtil.h @@ -0,0 +1,35 @@ +#ifndef SPEAKERUTIL_H +#define SPEAKERUTIL_H + +#include +#include +#include + +class SpeakerUtil : public QObject +{ + Q_OBJECT +public: + ~SpeakerUtil() {} + SpeakerUtil(const SpeakerUtil&)=delete; + SpeakerUtil& operator=(const SpeakerUtil&)=delete; + + static SpeakerUtil& getInstance() { + static SpeakerUtil instance; + return instance; + } + + void speak(QString content); + void sayIdentifySuccessZhCn(); + void sayIdentifyFailureZhCn(); + +private: + explicit SpeakerUtil(QObject *parent = nullptr); + + QTextToSpeech * tts; + QSoundEffect effect; + +signals: + +}; + +#endif // SPEAKERUTIL_H diff --git a/utils/TimeCounterUtil.cpp b/utils/TimeCounterUtil.cpp new file mode 100644 index 0000000..78c3d69 --- /dev/null +++ b/utils/TimeCounterUtil.cpp @@ -0,0 +1,9 @@ +#include "TimeCounterUtil.h" + +TimeCounterUtil::TimeCounterUtil() +{ + faceCapCounter = new QTimer(); + irisCapCounter = new QTimer(); + clockCounter = new QTimer(); + videoCounter = new QTimer(); +} diff --git a/utils/TimeCounterUtil.h b/utils/TimeCounterUtil.h new file mode 100644 index 0000000..91f8da0 --- /dev/null +++ b/utils/TimeCounterUtil.h @@ -0,0 +1,30 @@ +#ifndef TIMECOUNTERUTIL_H +#define TIMECOUNTERUTIL_H + +#include +#include + +class TimeCounterUtil : public QObject +{ +public: + + ~TimeCounterUtil() {}; + TimeCounterUtil(const TimeCounterUtil&)=delete; + TimeCounterUtil& operator=(const TimeCounterUtil&)=delete; + + static TimeCounterUtil& getInstance() { + static TimeCounterUtil instance; + return instance; + } + + QTimer * faceCapCounter; + QTimer * irisCapCounter; + QTimer * clockCounter; + QTimer * videoCounter; // show video images on main window + +private: + TimeCounterUtil(); + +}; + +#endif // TIMECOUNTERUTIL_H diff --git a/utils/UtilInclude.h b/utils/UtilInclude.h new file mode 100644 index 0000000..bea17be --- /dev/null +++ b/utils/UtilInclude.h @@ -0,0 +1,15 @@ +#ifndef UTILINCLUDE_H +#define UTILINCLUDE_H + +#include +#include "ByteUtil.h" +#include "ImageUtil.h" +#include "LogUtil.h" +#include "SettingConfig.h" +#include "SocketClientUtil.h" +#include "SpeakerUtil.h" +#include "TimeCounterUtil.h" +//#include "UDPClientUtil.h" +//#include "HttpRequestUtil.h" + +#endif // UTILINCLUDE_H diff --git a/utils/id/IdWorker.h b/utils/id/IdWorker.h new file mode 100644 index 0000000..079bbcb --- /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..4fc555a --- /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..daad45c --- /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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..8bd1b8c --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,37 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + void closeConnect(); + QByteArray getResponse(); + void resetRecvBuffer(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + void socketReconnect(); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..933809c --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,35 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); + + // 循环播放 + effect.setLoopCount(QSoundEffect::Null); + // 设置音量,0-1 + effect.setVolume(1.0f); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} + +void SpeakerUtil::sayIdentifySuccessZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recSuccess.wav")); + effect.play(); +} + +void SpeakerUtil::sayIdentifyFailureZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recFailureRetry.wav")); + effect.play(); +} diff --git a/utils/SpeakerUtil.h b/utils/SpeakerUtil.h new file mode 100644 index 0000000..02b221b --- /dev/null +++ b/utils/SpeakerUtil.h @@ -0,0 +1,35 @@ +#ifndef SPEAKERUTIL_H +#define SPEAKERUTIL_H + +#include +#include +#include + +class SpeakerUtil : public QObject +{ + Q_OBJECT +public: + ~SpeakerUtil() {} + SpeakerUtil(const SpeakerUtil&)=delete; + SpeakerUtil& operator=(const SpeakerUtil&)=delete; + + static SpeakerUtil& getInstance() { + static SpeakerUtil instance; + return instance; + } + + void speak(QString content); + void sayIdentifySuccessZhCn(); + void sayIdentifyFailureZhCn(); + +private: + explicit SpeakerUtil(QObject *parent = nullptr); + + QTextToSpeech * tts; + QSoundEffect effect; + +signals: + +}; + +#endif // SPEAKERUTIL_H diff --git a/utils/TimeCounterUtil.cpp b/utils/TimeCounterUtil.cpp new file mode 100644 index 0000000..78c3d69 --- /dev/null +++ b/utils/TimeCounterUtil.cpp @@ -0,0 +1,9 @@ +#include "TimeCounterUtil.h" + +TimeCounterUtil::TimeCounterUtil() +{ + faceCapCounter = new QTimer(); + irisCapCounter = new QTimer(); + clockCounter = new QTimer(); + videoCounter = new QTimer(); +} diff --git a/utils/TimeCounterUtil.h b/utils/TimeCounterUtil.h new file mode 100644 index 0000000..91f8da0 --- /dev/null +++ b/utils/TimeCounterUtil.h @@ -0,0 +1,30 @@ +#ifndef TIMECOUNTERUTIL_H +#define TIMECOUNTERUTIL_H + +#include +#include + +class TimeCounterUtil : public QObject +{ +public: + + ~TimeCounterUtil() {}; + TimeCounterUtil(const TimeCounterUtil&)=delete; + TimeCounterUtil& operator=(const TimeCounterUtil&)=delete; + + static TimeCounterUtil& getInstance() { + static TimeCounterUtil instance; + return instance; + } + + QTimer * faceCapCounter; + QTimer * irisCapCounter; + QTimer * clockCounter; + QTimer * videoCounter; // show video images on main window + +private: + TimeCounterUtil(); + +}; + +#endif // TIMECOUNTERUTIL_H diff --git a/utils/UtilInclude.h b/utils/UtilInclude.h new file mode 100644 index 0000000..bea17be --- /dev/null +++ b/utils/UtilInclude.h @@ -0,0 +1,15 @@ +#ifndef UTILINCLUDE_H +#define UTILINCLUDE_H + +#include +#include "ByteUtil.h" +#include "ImageUtil.h" +#include "LogUtil.h" +#include "SettingConfig.h" +#include "SocketClientUtil.h" +#include "SpeakerUtil.h" +#include "TimeCounterUtil.h" +//#include "UDPClientUtil.h" +//#include "HttpRequestUtil.h" + +#endif // UTILINCLUDE_H diff --git a/utils/id/IdWorker.h b/utils/id/IdWorker.h new file mode 100644 index 0000000..079bbcb --- /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..4fc555a --- /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..daad45c --- /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..4b2967f --- /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 index fab7372..1358d83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,73 +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 - +# 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/AppConstants.h b/AppConstants.h new file mode 100644 index 0000000..716094a --- /dev/null +++ b/AppConstants.h @@ -0,0 +1,35 @@ +#ifndef APPCONSTANTS_H +#define APPCONSTANTS_H + +class AppConstants +{ +public: + enum WidgeFrameName + { +// MAIN_PAGE = 0, // 主页面 +// PERSON_LIST_FORM = 1, // 人员列表页面 +// ADD_PERSON_FORM = 2, // 添加人员页面 +// ADD_PERSON_CAPTURE_FACE = 21, // 添加/编辑人员时进行人脸拍图 +// ADD_PERSON_CAPTURE_IRIS = 22, // 添加/编辑人员时进行虹膜拍图 +// SETTING_FORM = 3, // 设置页面 +// RECOGNIZE_RESULT_FORM = 4, // 识别结果界面 + IDENTIFY_FORM = 5, // 识别界面 含识别中 和 识别结果 + LOCKSCREEN_FORM = 6 // 锁屏待机界面 + }; + + enum ApplicationState + { + STATE_WAIT = 0, // 待机 + STATE_WORKING = 1, // 工作状态 + STATE_IDENTIFYING = 2, // 识别中 + }; + + enum IdentifyType + { + IRIS = 1, // 虹膜识别 + FACE = 2, // 人脸识别 + FINGER = 3, // 指纹识别 + }; +}; + +#endif // APPCONSTANTS_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index 5d7fea6..e5f8c61 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -1,34 +1,59 @@ -QT += core gui - -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 - -SOURCES += \ - main.cpp \ - IdentifyForm.cpp - -HEADERS += \ - IdentifyForm.h - -FORMS += \ - IdentifyForm.ui - -TRANSLATIONS += \ - CasicIrisIdentify_zh_CN.ts - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui sql network texttospeech multimedia + +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("device/device.pri") +include("utils/utils.pri") + +SOURCES += main.cpp +SOURCES += ProMemory.cpp +SOURCES += MainWindowForm.cpp +SOURCES += IdentifyForm.cpp +SOURCES += LockScreenForm.cpp + +HEADERS += AppConstants.h +HEADERS += ProMemory.h +HEADERS += MainWindowForm.h +HEADERS += IdentifyForm.h +HEADERS += LockScreenForm.h + +FORMS += MainWindowForm.ui +FORMS += IdentifyForm.ui +FORMS += LockScreenForm.ui + +RESOURCES += resource.qrc + +DISTFILES += conf/config.ini + +#INCLUDEPATH += include/spdlog +#DEPENDPATH += include/spdlog + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +INCLUDEPATH += $$PWD/Eigen + +unix:!macx: LIBS += -L$$PWD/lib/daheng/ -lgxiapi + +INCLUDEPATH += $$PWD/include/daheng +DEPENDPATH += $$PWD/include/daheng + +LIBS += $(shell pkg-config opencv4 --libs) +INCLUDEPATH += /usr/include/opencv4 diff --git a/CasicIrisIdentify_zh_CN.ts b/CasicIrisIdentify_zh_CN.ts deleted file mode 100644 index 5526fe4..0000000 --- a/CasicIrisIdentify_zh_CN.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/IdentifyForm.cpp b/IdentifyForm.cpp index 348a897..7abffa4 100644 --- a/IdentifyForm.cpp +++ b/IdentifyForm.cpp @@ -1,15 +1,161 @@ -#include "IdentifyForm.h" -#include "ui_IdentifyForm.h" - -IdentifyForm::IdentifyForm(QWidget *parent) - : QWidget(parent) - , ui(new Ui::IdentifyForm) -{ - ui->setupUi(this); -} - -IdentifyForm::~IdentifyForm() -{ - delete ui; -} - +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IdentifyForm.h" +#include "ui_IdentifyForm.h" + +IdentifyForm::IdentifyForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::IdentifyForm) +{ + ui->setupUi(this); + + ui->wgtIdentifying->setCurrentIndex(0); + + ui->labelIdenTips->setText(""); + ui->labelFailTips->setText("识 别 失 败\n请 重 试"); +} + +IdentifyForm::~IdentifyForm() +{ + delete ui; +} + +void IdentifyForm::updateIdentifyTips(QString tips) +{ + ui->labelIdenTips->setText(tips); +} + +void IdentifyForm::drawIrisImageOnFrame() +{ + // If acquisition did not started + if (!ProMemory::getInstance().irisCamCtrl->m_bAcquisitionStart) + { + return; + } + + // Get Image from image show queue, if image show queue is empty, return directly + QImage* qobjImgShow = ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PopFrontFromShowImageDeque(); + if(qobjImgShow == NULL) + { + return; + } + + QImage objImgGray = qobjImgShow->convertToFormat(QImage::Format_Grayscale8); + + // 图像逆时针旋转90度 + objImgGray = objImgGray.transformed(QTransform().rotate(SettingConfig::getInstance().DEBUG_CAMERA_ROTATE.toInt())); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + irisInfo.postProcSucc = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(objImgGray); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + // Display is finished, push back image buffer to buffer queue + ProMemory::getInstance().irisCamCtrl->m_pobjAcqThread->PushBackToEmptyBufferDeque(qobjImgShow); + + // 只在识别界面才显示画面 + if (ui->wgtIdentifying->currentIndex() == 0) { + // Display the image + QImage imgDisp = objImgGray.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + ui->labelVideo->setPixmap(QPixmap::fromImage(imgDisp)); + } + + return; +} + +void IdentifyForm::showRecogFailure() +{ + SpeakerUtil::getInstance().sayIdentifyFailureZhCn(); + LOG_INFO("识别失败,请重试"); + + ui->wgtIdentifying->setCurrentIndex(2); + + ProMemory::getInstance().clearIrisQueue(); + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + + QTimer::singleShot(SettingConfig::getInstance().FAILURE_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} + +void IdentifyForm::showRecognizeResult(QString personId) +{ + QVariantMap matched = personDao.findRecordById(personId); + + QString deptName = matched.value("deptname").toString(); + QString personName = matched.value("name").toString(); + SpeakerUtil::getInstance().sayIdentifySuccessZhCn(); + LOG_INFO(QString("识别成功,%1%2").arg(deptName).arg(personName).toStdString()); + + ui->wgtIdentifying->setCurrentIndex(1); + + // 查询并显示人员信息 + ui->labelName->setText(""); + ui->labelName->setText(personName); + ui->labelGender->setText(""); + ui->labelGender->setText(CacheManager::getInstance().getGenderName().value(matched.value("gender").toString()).toString()); + ui->labelDept->setText(""); + ui->labelDept->setText(deptName); + ui->labelTs->setText(""); + ui->labelTs->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")); + + // 查询并显示头像 + QPixmap photo; + QString photoPath = QString("images/%1").arg(matched.value("avatar").toString()); + bool succ = photo.load(photoPath); + if (succ == false) { + photo.load(":/images/photo.png"); + } + + // 缩放到合适的尺寸 + float rp = photo.width() * 1.0 / photo.height() * 1.0; // 图片的比例 + float rl = ui->labelPhoto->width() * 1.0 / ui->labelPhoto->height() * 1.0; // 显示框的比例 + + if (rp <= rl) { + // 如果图片的比例小则缩放到显示框的高度 + photo = photo.scaledToHeight(ui->labelPhoto->height()); + } else { + // 如果图片的比例大则缩放到显示框的宽度 + photo = photo.scaledToWidth(ui->labelPhoto->width()); + } + // 显示 + ui->labelPhoto->setPixmap(photo); + + // 执行数据库操作 + QVariantMap record; + record.insert("person_id", personId); + record.insert("date_time", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + record.insert("rec_type", AppConstants::IdentifyType::IRIS); // 虹膜识别; + record.insert("dev_code", SettingConfig::getInstance().DEVICE_CODE); + record.insert("debug_info", CasicIrisRecState::getInstance().toString()); + recordDao.save(record); + LOG_INFO("识别成功记录保存成功"); + + // 返回工作状态和界面 + QTimer::singleShot(SettingConfig::getInstance().SUCCESS_TIPS_LAST, [=](){ + ProMemory::getInstance().irisCamCtrl->startCapture(); + ProMemory::getInstance().irisRecogPro->setWorking(true); + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; + ui->labelIdenTips->setText(""); + + // 清除上一次识别的状态信息 + CasicIrisRecState::getInstance().restRecognize(); + ui->wgtIdentifying->setCurrentIndex(0); + }); +} diff --git a/IdentifyForm.h b/IdentifyForm.h index fc857a1..8196c19 100644 --- a/IdentifyForm.h +++ b/IdentifyForm.h @@ -1,21 +1,39 @@ -#ifndef IDENTIFYFORM_H -#define IDENTIFYFORM_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class IdentifyForm; } -QT_END_NAMESPACE - -class IdentifyForm : public QWidget -{ - Q_OBJECT - -public: - IdentifyForm(QWidget *parent = nullptr); - ~IdentifyForm(); - -private: - Ui::IdentifyForm *ui; -}; -#endif // IDENTIFYFORM_H +#ifndef IDENTIFYFORM_H +#define IDENTIFYFORM_H + +#include +#include + +#include "dao/SysPersonDao.h" +#include "dao/RecognitionRecordsDao.h" +#include "dao/util/CacheManager.h" +#include "utils/UtilInclude.h" +#include "ProMemory.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class IdentifyForm; } +QT_END_NAMESPACE + +class IdentifyForm : public QWidget +{ + Q_OBJECT + +public: + IdentifyForm(QWidget *parent = nullptr); + ~IdentifyForm(); + + void updateIdentifyTips(QString tips); + +public slots: + void drawIrisImageOnFrame(); + + void showRecogFailure(); + void showRecognizeResult(QString personId); + +private: + Ui::IdentifyForm *ui; + SysPersonDao personDao; + RecognitionRecordsDao recordDao; + +}; +#endif // IDENTIFYFORM_H diff --git a/IdentifyForm.ui b/IdentifyForm.ui index c72a36f..def6ccf 100644 --- a/IdentifyForm.ui +++ b/IdentifyForm.ui @@ -1,19 +1,199 @@ - - - IdentifyForm - - - - 0 - 0 - 800 - 600 - - - - IdentifyForm - - - - - + + + IdentifyForm + + + + 0 + 0 + 600 + 800 + + + + IdentifyForm + + + + + 0 + 0 + 600 + 800 + + + + 2 + + + + + + 105 + 35 + 393 + 512 + + + + + + + Qt::AlignCenter + + + + + + 0 + 650 + 600 + 80 + + + + background: transparent; + + + Qt::AlignCenter + + + + + + background: url(":/images/bg_success.png"); + + + + + 100 + 50 + 400 + 305 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + 200 + 400 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 498 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 595 + 300 + 60 + + + + background: transparent; + + + + + + + + + 200 + 691 + 300 + 60 + + + + background: transparent; + + + + + + + + + + + 100 + 50 + 400 + 300 + + + + background: transparent; + + + + + + :/images/iconWarn.png + + + Qt::AlignCenter + + + + + + 150 + 420 + 300 + 300 + + + + background: transparent; + + + + + + Qt::AlignCenter + + + + + + + + + + diff --git a/LockScreenForm.cpp b/LockScreenForm.cpp new file mode 100644 index 0000000..c33cbe8 --- /dev/null +++ b/LockScreenForm.cpp @@ -0,0 +1,31 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "LockScreenForm.h" +#include "ui_LockScreenForm.h" + +LockScreenForm::LockScreenForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::LockScreenForm) +{ + ui->setupUi(this); +} + +LockScreenForm::~LockScreenForm() +{ + delete ui; +} + +void LockScreenForm::showLockScreenTime() +{ + QDateTime now = QDateTime::currentDateTime(); + ui->labelLockHour->setText(now.time().toString("HH")); + ui->labelLockMinute->setText(now.time().toString("mm")); + ui->labelLockDate->setText(now.date().toString("MM月dd日 dddd")); + if (now.time().second() % 2 == 0) { + ui->labelLockSep->hide(); + } else { + ui->labelLockSep->show(); + } +} diff --git a/LockScreenForm.h b/LockScreenForm.h new file mode 100644 index 0000000..f53f308 --- /dev/null +++ b/LockScreenForm.h @@ -0,0 +1,28 @@ +#ifndef LOCKSCREENFORM_H +#define LOCKSCREENFORM_H + +#include +#include + +#include "utils/UtilInclude.h" + +namespace Ui { +class LockScreenForm; +} + +class LockScreenForm : public QWidget +{ + Q_OBJECT + +public: + explicit LockScreenForm(QWidget *parent = nullptr); + ~LockScreenForm(); + +public slots: + void showLockScreenTime(); + +private: + Ui::LockScreenForm *ui; +}; + +#endif // LOCKSCREENFORM_H diff --git a/LockScreenForm.ui b/LockScreenForm.ui new file mode 100644 index 0000000..81e12d1 --- /dev/null +++ b/LockScreenForm.ui @@ -0,0 +1,113 @@ + + + LockScreenForm + + + + 0 + 0 + 600 + 800 + + + + Form + + + + + 0 + 270 + 600 + 100 + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 285 + 130 + 30 + 128 + + + + + Microsoft YaHei UI Light + 96 + + + + : + + + Qt::AutoText + + + false + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + 320 + 120 + 280 + 150 + + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + 275 + 700 + 50 + 50 + + + + + + + :/images/lock.png + + + true + + + Qt::AlignCenter + + + + + + + + diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp new file mode 100644 index 0000000..1a6e8fb --- /dev/null +++ b/MainWindowForm.cpp @@ -0,0 +1,185 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "MainWindowForm.h" +#include "ui_MainWindowForm.h" + +MainWindowForm::MainWindowForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::MainWindowForm) +{ + ui->setupUi(this); + + // 设置窗口透明和大小、位置 + this->setWindowFlags(Qt::FramelessWindowHint); +// this->move(1400, 15); + this->move(0, 0); + this->resize(SettingConfig::getInstance().WINDOW_WIDTH, SettingConfig::getInstance().WINDOW_HEIGHT); + + // 通过调色板的颜色来设置窗口的统一背景色 + qApp->setPalette(QPalette(QColor(SettingConfig::getInstance().WINDOW_BACKGROUND_COLOR))); + + // 加载css文件设置控件样式 + QFile file(QApplication::applicationDirPath() + "/qss/main.css"); + if (file.open(QFile::ReadOnly)) + { + QString qssStr = QLatin1String(file.readAll()); + this->setStyleSheet(qssStr); + file.close(); + } + + // 初始化虹膜库 + ProMemory::getInstance().initIrisFeatures(); + + // 初始化各个界面的form + initFormsPtr(); + + // 设置标题文字 + ui->labelCopyright->setText(SettingConfig::getInstance().WINDOW_RIGHTS); + ui->labelVersion->setText(SettingConfig::getInstance().WINDOW_VERSION); + + ui->labelMainTime->setText(QTime::currentTime().toString("HH:mm:ss")); + ui->labelMainDate->setText(QDate::currentDate().toString("yyyy-MM-dd dddd")); + ui->labelLogo->hide(); + + // 初始化更新界面的定时器 + // 每秒执行一次 + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + TimeCounterUtil::getInstance().clockCounter->start(1000); + + // 初始化虹膜相机控制 + ProMemory::getInstance().irisCamCtrl = new IrisCameraController(); + ProMemory::getInstance().irisCamCtrl->openIrisCamera(); + + connect(TimeCounterUtil::getInstance().videoCounter, &QTimer::timeout, identifyForm, &IdentifyForm::drawIrisImageOnFrame); + TimeCounterUtil::getInstance().videoCounter->start(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + LOG_INFO(QString("应用程序启动成功[Application Startup Success]").toStdString()); + qDebug() << QString("应用程序启动成功[Application Startup Success]"); + + // 启动后显示视频画面 工作状态 未开始识别 + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; // 启动后显示视频画面 + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WORKING; // 启动后处于工作状态 + ui->wdgtStatced->setCurrentWidget(identifyForm); + + // 开始虹膜相机拍图 + ProMemory::getInstance().irisCamCtrl->startCapture(); + + // 识别线程开始工作 + initIrisRecogThread(); // 虹膜识别线程 + ProMemory::getInstance().irisRecogPro->setWorking(true); +} + +MainWindowForm::~MainWindowForm() +{ + ProMemory::getInstance().irisRecogPro->setWorking(false); + ProMemory::getInstance().irisRecogPro->exitThread(); + ProMemory::getInstance().irisRecogPro->deleteLater(); + ProMemory::getInstance().irisRecogPro->wait(); + + delete ui; + delete ProMemory::getInstance().irisRecogPro; +} + +void MainWindowForm::lockScreenHandler() +{ + ui->wdgtStatced->setCurrentWidget(lockScreenForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::LOCKSCREEN_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_WAIT; // App状态 = 待机 + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(2.5f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().LOCK_FRAME_INTERVAL); + + ui->labelMainTime->hide(); + ui->labelMainDate->hide(); + ui->labelLogo->show(); + ui->labelLogo->setPixmap(QPixmap("images/logo.png")); + + LOG_INFO("长时间未找到眼睛,开始待机"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); +} + +void MainWindowForm::startIdentifyHandler() +{ + ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; + ProMemory::getInstance().appState = AppConstants::ApplicationState::STATE_IDENTIFYING; // App状态 = 识别中 + identifyForm->updateIdentifyTips("识 别 中"); + + ProMemory::getInstance().irisCamCtrl->SetCameraFrameRate(10.0f); + TimeCounterUtil::getInstance().irisCapCounter->setInterval(SettingConfig::getInstance().IRIS_FRAME_INTERVAL); + + ui->labelMainTime->show(); + ui->labelMainDate->show(); + ui->labelLogo->hide(); + + LOG_INFO("找到眼睛,结束待机,开始工作"); + + // 重新绑定每秒的定时信号和槽函数 + disconnect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, lockScreenForm, &LockScreenForm::showLockScreenTime); + connect(TimeCounterUtil::getInstance().clockCounter, &QTimer::timeout, this, &MainWindowForm::updateBannerTime); +} + +void MainWindowForm::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Escape: + QTimer::singleShot(100, qApp, [=](){ + QApplication::quit(); + }); + + default: + QWidget::keyPressEvent(event); + } +} + +void MainWindowForm::initFormsPtr() +{ + // 初始化各个form + lockScreenForm = new LockScreenForm(this); + identifyForm = new IdentifyForm(this); + + // 将form添加到statcked widget中 + ui->wdgtStatced->addWidget(identifyForm); + ui->wdgtStatced->addWidget(lockScreenForm); + + // 绑定按钮函数 +} + +void MainWindowForm::initIrisRecogThread() +{ + // 虹膜识别处理过程 + ProMemory::getInstance().irisRecogPro = new IrisRecogProcess(this); + + // 绑定信号与槽函数 + // 识别成功 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::findMatchedIris, identifyForm, &IdentifyForm::showRecognizeResult); + + // 识别失败 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::failedMatchedIris, identifyForm, &IdentifyForm::showRecogFailure); + + // 找到眼睛 开始识别 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::startIdentifyIris, this, &MainWindowForm::startIdentifyHandler); + + // 持续没找到目标 待机 + connect(ProMemory::getInstance().irisRecogPro, &IrisRecogProcess::backToLockScreen, this, &MainWindowForm::lockScreenHandler); + + ProMemory::getInstance().irisRecogPro->start(); +} + +void MainWindowForm::updateBannerTime() +{ + QDateTime now = QDateTime::currentDateTime(); + if (ProMemory::getInstance().widgeFrame == AppConstants::WidgeFrameName::IDENTIFY_FORM) + { + ui->labelMainTime->setText(now.toString("HH:mm:ss")); + + if (now.toString("HH:mm:ss") == "00:00:00") { + ui->labelMainDate->setText(now.toString("yyyy-MM-dd dddd")); + } + } +} diff --git a/MainWindowForm.h b/MainWindowForm.h new file mode 100644 index 0000000..9858b2a --- /dev/null +++ b/MainWindowForm.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOWFORM_H +#define MAINWINDOWFORM_H + +#include +#include +#include + +#include "utils/UtilInclude.h" +#include "ProMemory.h" +#include "LockScreenForm.h" +#include "IdentifyForm.h" + +namespace Ui { +class MainWindowForm; +} + +class MainWindowForm : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowForm(QWidget *parent = nullptr); + ~MainWindowForm(); + +public slots: + void lockScreenHandler(); + void startIdentifyHandler(); + +private: + Ui::MainWindowForm *ui; + LockScreenForm * lockScreenForm; + IdentifyForm * identifyForm; + + void keyPressEvent(QKeyEvent *event); + + void initFormsPtr(); + void initIrisRecogThread(); + +private slots: + void updateBannerTime(); +}; + +#endif // MAINWINDOWFORM_H diff --git a/MainWindowForm.ui b/MainWindowForm.ui new file mode 100644 index 0000000..79949db --- /dev/null +++ b/MainWindowForm.ui @@ -0,0 +1,123 @@ + + + MainWindowForm + + + + 0 + 0 + 600 + 1024 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + background: url(":images/bg_title.png") + + + + + 0 + 0 + 600 + 84 + + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 0 + 84 + 600 + 40 + + + + + + + Qt::AlignCenter + + + + + + 246 + 20 + 108 + 72 + + + + + + + images/logo.png + + + true + + + Qt::AlignCenter + + + + + + + + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/ProMemory.cpp b/ProMemory.cpp new file mode 100644 index 0000000..0eacecb --- /dev/null +++ b/ProMemory.cpp @@ -0,0 +1,94 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ProMemory.h" + +ProMemory::ProMemory() +{ + +} + +ProMemory::~ProMemory() +{ + +} + +void ProMemory::pushCasicIris(CasicIrisInfo irisInfo) +{ + mutex.lock(); + if (this->irisQueue.size() > 10) + { + QStack empty; + std::swap(empty, irisQueue); + } + irisQueue.push(irisInfo); + mutex.unlock(); +} +CasicIrisInfo ProMemory::popCasicIris() +{ + CasicIrisInfo result; + + mutex.lock(); + if (this->irisQueue.size() > 0) + { + result = irisQueue.pop(); + } + mutex.unlock(); + + return result; +} + +int ProMemory::getIrisQueueSize() +{ + int size = 0; + + mutex.lock(); + size = this->irisQueue.size(); + mutex.unlock(); + + return size; +} +bool ProMemory::isIrisQueueEmpty() +{ + bool empty = true; + + mutex.lock(); + empty = this->irisQueue.isEmpty(); + mutex.unlock(); + + return empty; +} +void ProMemory::clearIrisQueue() +{ + mutex.lock(); + QStack empty; + std::swap(empty, irisQueue); + mutex.unlock(); +} + +void ProMemory::initIrisFeatures() +{ + IrisDataDao irisDao; + + QVector irisDataList = irisDao.findAllRecord(); + for (QVariantMap iris : irisDataList) { + CasicIrisFeature featureLeft; + featureLeft.personId = iris.value("person_id").toString(); + featureLeft.irisFeatureCode = iris.value("left_iris_code1").toByteArray(); + + CasicIrisFeature featureRight; + featureRight.personId = iris.value("person_id").toString(); + featureRight.irisFeatureCode = iris.value("right_iris_code1").toByteArray(); + + irisFeatures.append(featureLeft); + irisFeatures.append(featureRight); + } + + LOG_INFO(QString("加载虹膜特征值数据成功 %1个[IRIS Features loaded Success]").arg(irisFeatures.size()).toStdString()); +} + +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h new file mode 100644 index 0000000..cf1fe42 --- /dev/null +++ b/ProMemory.h @@ -0,0 +1,57 @@ +#ifndef PROMEMORY_H +#define PROMEMORY_H + +#include +#include + +#include "AppConstants.h" +#include "casic/iris/CasicIrisInfo.h" +#include "dao/IrisDataDao.h" +#include "utils/SocketClientUtil.h" + +#include "device/IrisCameraController.h" +#include "device/iris/IrisRecogProcess.h" + +class IrisCameraController; +class IrisRecogProcess; + +class ProMemory +{ +public: + ~ProMemory(); + ProMemory(const ProMemory&)=delete; + ProMemory& operator=(const ProMemory&)=delete; + + static ProMemory& getInstance() { + static ProMemory instance; + return instance; + } + + void pushCasicIris(CasicIrisInfo irisInfo); + CasicIrisInfo popCasicIris(); + int getIrisQueueSize(); + bool isIrisQueueEmpty(); + void clearIrisQueue(); + + volatile int widgeFrame = 0; // 当前显示的界面 + volatile int appState = 0; // 当前程序所处的状态 + + void initIrisFeatures(); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); + + IrisCameraController * irisCamCtrl; + IrisRecogProcess * irisRecogPro; + + SocketClientUtil client; + +private: + ProMemory(); + + QMutex mutex; + + QStack irisQueue; // 虹膜信息队列 + QVector irisFeatures; // 虹膜特征值集合 +}; + +#endif // PROMEMORY_H 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..e3c0735 --- /dev/null +++ b/casic/iris/CasicIrisInfo.h @@ -0,0 +1,38 @@ +#ifndef CASICIRISINFO_H +#define CASICIRISINFO_H + +#include +#include +#include "CasicSegResult.h" + +struct CasicIrisInfo +{ + // 是否有眼睛, 默认为false + bool hasEye = false; + + // post process + bool postProcSucc = 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..5803f44 --- /dev/null +++ b/casic/iris/CasicIrisInterface.cpp @@ -0,0 +1,234 @@ +#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); + + if (irisInfo.matData.empty() == true) { + irisInfo.hasEye = false; + return irisInfo; + } + + // ★分类器对象调用 + 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 = 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.postProcSucc = false; + irisInfo.matData = cutSize; + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisQualityAssess(CasicIrisInfo irisInfo) +{ + + return irisInfo; +} + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ +// std::vector irisCircle, pupilCircle; // x,y,r + std::vector irisCircle(0); // x,y,r + std::vector pupilCircle(0); // 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) + { + irisInfo.postProcSucc = false; + 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 + irisInfo.postProcSucc = false; + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (imageNorm.empty() == true) { + irisInfo.postProcSucc = false; + return irisInfo; + } + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + if (irisInfo.maskNorm.empty() == true) + { + irisInfo.postProcSucc = false; + return irisInfo; + } +// std::cout << "finish normalize" << std::endl; +// cv::imwrite("/home/nvidia/irisLogs/image-norm.bmp", imageNorm); + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + irisInfo.postProcSucc = true; + + 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..7f91d68 --- /dev/null +++ b/casic/iris/CasicIrisInterface.h @@ -0,0 +1,54 @@ +#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 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..c8d3bbb --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,499 @@ +#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++) { + if (yo_i(i, j) < 0 || xo_i(i, j) < 0) { + cv::Mat empty; + return empty; + } + 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); + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::endl; + + 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; + +// std::cout << "array: " << row << ", " << pos.second << std::endl; + 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; + } + } + } + +// std::cout << "shift " << s << " " << mean << "/" << count << " : " << mean / count << std::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()); + + cv::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; + } + } + +// std::cout << "reduce iris code, length: " << width * height << std::endl; + + 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; + } + } + +// std::cout << "reduce iris mask, length: " << width * height << std::endl; + + return irisMask; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..3d5193b --- /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..e81b0a6 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,294 @@ +#include "CasicSegPostProcess.h" + +#include +#include +#include +#include "utils/LogUtil.h" + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = cv::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; + cv::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; + cv::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); + + cv::threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + cv::threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + cv::threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(iris, iris, cv::MORPH_CLOSE, element); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-thre.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ +// 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; + cv::findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + +// LOG_INFO(QString("outPoints size: %1").arg(outPoints.size()).toStdString()); + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + cv::findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + +// LOG_INFO(QString("innerPoints size: %1, %2").arg(innerPoints.size()).arg(innerCounter.size()).toStdString()); + + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); +// LOG_INFO(QString("innerPoints: %1, %2, %3, %4").arg(c.begin()->x).arg(c.begin()->y).arg(c.end()->x).arg(c.end()->y).toStdString()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + +// LOG_INFO(QString("innerPoints size: %1").arg(innerPoints.size()).toStdString()); +/* + std::string pupilbest = QString("%1/%2/%3-pupil-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisbest = QString("%1/%2/%3-iris-best.bmp").arg("/home/casic/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilbest, bestPupil); + cv::imwrite(irisbest, bestIris); +*/ +// std::cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << std::endl; +// std::cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << std::endl; +// LOG_INFO(QString("iris: %1, %2, %3").arg(iris_x).arg(iris_y).arg(iris_r).toStdString()); +// LOG_INFO(QString("pupil: %1, %2, %3").arg(pupil_x).arg(pupil_y).arg(pupil_r).toStdString()); + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + cv::circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + cv::circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + 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..a3b6c86 --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include + +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/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..1697b34 --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,55 @@ +[window] +#界面窗口宽/高(px) +width=600 +height=1024 +#统一背景色 +backgroundColor="#FFFFFF" +#标题 版权所有 版本号 +title= +copyright=中国航天科工二院二〇三所 +version=1.0.0 +deviceCode=792023000001 + +[camera] +#虹膜相机取一幅画面的时间间隔(ms) +irisFrameInterval=100 +#待机时拍摄帧率(ms) +lockFrameInterval=400 +#虹膜相机拍摄宽度 / 高度 +irisFrameWidth=1080 +irisFrameHeight=1440 +#虹膜图像高度 / 高度 +irisWidth=640 +irisHeight=480 +#虹膜显示图像宽度 / 高度 +irisDisplayWidth=393 +irisDisplayHeight=512 + +[recognize] +#最大允许识别时间(ms) +maxMatchTime=10000 +#识别成功界面停留时间(ms) +successTipsLast=5000 +#识别失败界面停留时间(ms) +failureTipsLast=3000 +#虹膜识别最大尝试次数 +maxIrisTryCount=5 +#虹膜识别持续没有找到眼的最大次数 +maxEyeNotFoundCount=30 +#持续没找到目标而待机的最大次数 +maxFaceNotFoundCount=150 +#识别检测最小眼睛大小 +minEyeSize=320 + + +[log] +#日志文件位置 +logFile="logs//casic_log.log" +#日志级别 +logLevel="debug" + +[debug] +cameraRotate=90 +segmentIp=192.168.83.107 +saveImage=true +imageBasePath="logs" 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..1693c88 --- /dev/null +++ b/dao/BaseDao.h @@ -0,0 +1,32 @@ +#ifndef BASEDAO_H +#define BASEDAO_H + +#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..8ceb89d --- /dev/null +++ b/dao/IrisDataDao.cpp @@ -0,0 +1,213 @@ +#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_TRACE(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::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()); + + // 结束事务 + 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..c74dbbd --- /dev/null +++ b/dao/IrisDataDao.h @@ -0,0 +1,25 @@ +#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 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..19243b1 --- /dev/null +++ b/dao/SysPersonDao.cpp @@ -0,0 +1,375 @@ +#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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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()); +// result.insert("hasFace", query.value("face_valid").toString()); +// result.insert("hasIris", query.value("iris_valid").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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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; +} + +QVector SysPersonDao::findRecordsByProperties(QVariantMap conditions) +{ + // 新建查询 + 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'"; + QVariantMap::iterator it; + for (it = conditions.begin(); it != conditions.end(); it++) + { + sql += QString(" AND SYS_PERSON.%1 = %2").arg(it.key()).arg(it.value().toString()); + } + + // 执行查询 + 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()); + item.insert("hasFace", query.value("face_valid").toString()); + item.insert("hasIris", query.value("iris_valid").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..e02e5ba --- /dev/null +++ b/dao/SysPersonDao.h @@ -0,0 +1,27 @@ +#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); + QVector findRecordsByProperties(QVariantMap conditions); + + 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..5f3b7f3 --- /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/CacheManager.cpp b/dao/util/CacheManager.cpp new file mode 100644 index 0000000..e415d93 --- /dev/null +++ b/dao/util/CacheManager.cpp @@ -0,0 +1,12 @@ +#include "CacheManager.h" + +CacheManager::CacheManager() +{ + SysDictDao dictDao; + genderName = dictDao.findChildDictByCode("gender"); +} + +QVariantMap CacheManager::getGenderName() +{ + return genderName; +} diff --git a/dao/util/CacheManager.h b/dao/util/CacheManager.h new file mode 100644 index 0000000..5226744 --- /dev/null +++ b/dao/util/CacheManager.h @@ -0,0 +1,31 @@ +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include +#include +#include "dao/SysDeptDao.h" +#include "dao/SysDictDao.h" +#include "dao/SysPersonDao.h" +#include "dao/IrisDataDao.h" + +class CacheManager +{ +public: + ~CacheManager() {}; + CacheManager(const CacheManager&)=delete; + CacheManager& operator=(const CacheManager&)=delete; + + static CacheManager& getInstance() { + static CacheManager instance; + return instance; + } + + QVariantMap getGenderName(); + +private: + CacheManager(); + + QVariantMap genderName; +}; + +#endif // CACHEMANAGER_H diff --git a/dao/util/ConnectionManager.cpp b/dao/util/ConnectionManager.cpp new file mode 100644 index 0000000..fed7b8d --- /dev/null +++ b/dao/util/ConnectionManager.cpp @@ -0,0 +1,51 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "ConnectionManager.h" +#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()); + } else + { + LOG_ERROR(QString("打开数据库操作失败[Open Database Failed]").toStdString()); + } + } +} + +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..84647e3 --- /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/device/AcquisitionThread.cpp b/device/AcquisitionThread.cpp new file mode 100644 index 0000000..500f5b1 --- /dev/null +++ b/device/AcquisitionThread.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.cpp +\brief CAcquisitionThread Class implementation file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#include "AcquisitionThread.h" + +//---------------------------------------------------------------------------------- +/** +\Constructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::CAcquisitionThread(QObject *parent) : + QThread(parent), + m_ui64AcquisitionBufferNum(0), + m_nFrameCount(0), + m_bAcquisitionThreadFlag(false), + m_hDevice(NULL), + m_bSoftTriggerOn(false), + m_i64ColorFilter(0), + m_i64ImageMaxWidth(0), + m_i64ImageWidth(0), + m_i64ImageMaxHeight(0), + m_i64ImageHeight(0), + m_bColorFilter(false), + m_bColorCorrection(false), + m_bGammaRegulation(false), + m_bContrastRegulation(false), + m_i64ColorCorrection(0), + m_pGammaLut(NULL), + m_pContrastLut(NULL), + m_pstarrFrameBuffer(NULL), + m_pRaw8Image(NULL), + m_pImageElement0(NULL), + m_pImageElement1(NULL), + m_pImageElement2(NULL), + m_objParamMutex(QMutex::Recursive), + m_objDequeMutex(QMutex::Recursive) +{ + +} +//---------------------------------------------------------------------------------- +/** +\Destructor +*/ +//---------------------------------------------------------------------------------- +CAcquisitionThread::~CAcquisitionThread() +{ + // Release all resources + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + RELEASE_ALLOC_ARR(m_pRaw8Image); + + RELEASE_ALLOC_ARR(m_pGammaLut); + RELEASE_ALLOC_ARR(m_pContrastLut); + + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); +} + +//---------------------------------------------------------------------------------- +/** +\Main function of Acquisition-thread, Acquisition-thread run start from here +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::run() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + PROC_STATUS emProcStatus = PROC_SUCCESS; + uint32_t ui32FrameNum = 0; + + // Acquisition frame count reset + m_nFrameCount = 0; + + // Acquisition thread loop + while (m_bAcquisitionThreadFlag) + { + // Acquire ui32FrameElementNum frame data from buffer queue + emStatus = GXDQAllBufs(m_hDevice, m_pstarrFrameBuffer, m_ui64AcquisitionBufferNum, &ui32FrameNum, 1000); + + // Continue when GXDQAllBufs timeout, other error will quit acquisiton loop + if (emStatus != GX_STATUS_SUCCESS) + { + if (emStatus == GX_STATUS_TIMEOUT) + { + continue; + } + else + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + } + + // Return all bufs back when met the last frame is incomplete + if (m_pstarrFrameBuffer[ui32FrameNum - 1]->nStatus != GX_FRAME_STATUS_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + continue; + } + + // Get a buffer for process new image + QImage *pobjImageBuffer = PopFrontFromEmptyBufferDeque(); + // If buffer deque is empty, get one buffer from image show deque + if (pobjImageBuffer == NULL) + { + pobjImageBuffer = PopFrontFromShowImageDeque(); + } + + // Assign the address of the first pixel of the QImage to a temporary variable for image processing + unsigned char* pImageProcess = pobjImageBuffer->bits(); + + // Image processing, Raw to RGB24 and image improvment, if process failed put buffer back to buffer deque + emProcStatus = ImageProcess(m_pstarrFrameBuffer[ui32FrameNum - 1], pImageProcess); + if (emProcStatus != PROC_SUCCESS) + { + emStatus = GXQAllBufs(m_hDevice); + PushBackToEmptyBufferDeque(pobjImageBuffer); + break; + } + + // Image processing is done push processed buffer to show image deque + PushBackToShowImageDeque(pobjImageBuffer); + + // Put all buffers back to deque + emStatus = GXQAllBufs(m_hDevice); + if (emStatus != GX_STATUS_SUCCESS) + { + //Get Acquisition error and send it to main thread + GetAcquistionErrorString(emStatus); + break; + } + + // Get acquisition frame rate + for (uint32_t i = 0; i < ui32FrameNum; i++) + { + m_nFrameCount++; + } + } + + // Clear both deque when acquisition is stop + ClearDeque(); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to empty buffer deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToEmptyBufferDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objEmptyBufferDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Push back to show image deque +\param[in] pobjImage Buffer pointer to push back +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::PushBackToShowImageDeque(QImage* pobjImage) +{ + QMutexLocker locker(&m_objDequeMutex); + m_objShowImageDeque.push_back(pobjImage); + + return; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from empty buffer deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromEmptyBufferDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objEmptyBufferDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objEmptyBufferDeque.front(); + m_objEmptyBufferDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Pop front from show image deque +\param[in] +\param[out] +\return QImage* pobjImage If deque not empty return a buffer pointer + NULL If deque is empty return NULL +*/ +//---------------------------------------------------------------------------------- +QImage* CAcquisitionThread::PopFrontFromShowImageDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + if (m_objShowImageDeque.empty()) + { + return NULL; + } + + QImage* pobjImage = m_objShowImageDeque.front(); + m_objShowImageDeque.pop_front(); + + return pobjImage; +} + +//---------------------------------------------------------------------------------- +/** +\Clear both deque +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::ClearDeque() +{ + QMutexLocker locker(&m_objDequeMutex); + + m_objEmptyBufferDeque.clear(); + m_objShowImageDeque.clear(); + + return; +} + + +//---------------------------------------------------------------------------------- +/** +\Process Raw image to RGB image and do image improvment +\param[in] pstFrameBuffer Raw image acquired +\param[out] pImageProcess Processed image +\return PROC_STATUS PROC_SUCCESS process successed + PROC_FAIL process failed +*/ +//---------------------------------------------------------------------------------- +PROC_STATUS CAcquisitionThread::ImageProcess(const PGX_FRAME_BUFFER pstFrameBuffer, unsigned char* pImageProcess) +{ + VxInt32 emDxStatus = DX_OK; + + // Convert RAW8 or RAW16 image to RGB24 image + switch (pstFrameBuffer->nPixelFormat) + { + case GX_PIXEL_FORMAT_MONO8: + case GX_PIXEL_FORMAT_MONO8_SIGNED: + case GX_PIXEL_FORMAT_BAYER_GR8: + case GX_PIXEL_FORMAT_BAYER_RG8: + case GX_PIXEL_FORMAT_BAYER_GB8: + case GX_PIXEL_FORMAT_BAYER_BG8: + { + // Convert to the RGB + emDxStatus = DxRaw8toRGB24((unsigned char*)pstFrameBuffer->pImgBuf, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO10: + case GX_PIXEL_FORMAT_BAYER_GR10: + case GX_PIXEL_FORMAT_BAYER_RG10: + case GX_PIXEL_FORMAT_BAYER_GB10: + case GX_PIXEL_FORMAT_BAYER_BG10: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_2_9); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + case GX_PIXEL_FORMAT_MONO12: + case GX_PIXEL_FORMAT_BAYER_GR12: + case GX_PIXEL_FORMAT_BAYER_RG12: + case GX_PIXEL_FORMAT_BAYER_GB12: + case GX_PIXEL_FORMAT_BAYER_BG12: + { + // Convert to the Raw8 image + emDxStatus = DxRaw16toRaw8((unsigned char*)pstFrameBuffer->pImgBuf, m_pRaw8Image, m_i64ImageWidth, m_i64ImageHeight, DX_BIT_4_11); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + // Convert to the RGB24 image + emDxStatus = DxRaw8toRGB24((unsigned char*)m_pRaw8Image, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, + RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(m_i64ColorFilter), false); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + break; + } + default: + { + // Enter this branch when pixel format not support + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + // Image improvment params will changed in other thread, must being locked + QMutexLocker locker(&m_objParamMutex); + + int64_t i64ColorCorrection = m_bColorCorrection ? m_i64ColorCorrection : 0; + unsigned char* pGammaLut = m_bGammaRegulation ? m_pGammaLut : NULL; + unsigned char* pContrastLut = m_bContrastRegulation ? m_pContrastLut : NULL; + + if (i64ColorCorrection != 0 || pGammaLut != NULL || pContrastLut != NULL) + { + emDxStatus = DxImageImprovment(pImageProcess, pImageProcess, m_i64ImageWidth, m_i64ImageHeight, i64ColorCorrection, pContrastLut, pGammaLut); + if (emDxStatus != DX_OK) + { + emit SigImageProcError(emDxStatus); + return PROC_FAIL; + } + } + + return PROC_SUCCESS; +} + + +//---------------------------------------------------------------------------------- +/** +\Get device handle from main-thread +\param[in] +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetDeviceHandle(GX_DEV_HANDLE hDeviceHandle) +{ + m_hDevice = hDeviceHandle; +} + +//---------------------------------------------------------------------------------- +/** +\Alloc QImage resource for show frames on ImageLabel +\param[in] +\param[out] +\return bool true : Prepare success +\ false: Prepare fail +*/ +//---------------------------------------------------------------------------------- +bool CAcquisitionThread::PrepareForShowImg() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + int64_t i64ImageWidth = 0; + int64_t i64ImageHeight = 0; + + // Release PGX_FRAME_BUFFER array + RELEASE_ALLOC_ARR(m_pstarrFrameBuffer); + + // PGX_FRAME_DATA is pointer of GX_FRAME_DATA, pointer array for image acquisition + try + { + m_pstarrFrameBuffer = new PGX_FRAME_BUFFER[m_ui64AcquisitionBufferNum]; + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate PGX_FRAME_BUFFER array failed! "); + return false; + } + + // Get the type of Bayer conversion. whether is a color camera. + emStatus = GXIsImplemented(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_bColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Color image + if(m_bColorFilter) + { + emStatus = GXGetEnum(m_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &m_i64ColorFilter); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + } + + // Get the image width + emStatus = GXGetInt(m_hDevice, GX_INT_WIDTH, &i64ImageWidth); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Get the image height + emStatus = GXGetInt(m_hDevice, GX_INT_HEIGHT, &i64ImageHeight); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // If width or height is changed, realloc image buffer + if (i64ImageWidth != m_i64ImageWidth || i64ImageHeight != m_i64ImageHeight) + { + m_i64ImageWidth = i64ImageWidth; + m_i64ImageHeight = i64ImageHeight; + + RELEASE_ALLOC_ARR(m_pRaw8Image); + RELEASE_ALLOC_MEM(m_pImageElement0); + RELEASE_ALLOC_MEM(m_pImageElement1); + RELEASE_ALLOC_MEM(m_pImageElement2); + + try + { + // Allocate raw8 frame buffer for DxRaw16toRaw8 + m_pRaw8Image = new unsigned char[m_i64ImageWidth * m_i64ImageHeight]; + + // Allocate three QImage buffer for deque acquisition + m_pImageElement0 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement1 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + m_pImageElement2 = new QImage(m_i64ImageWidth, m_i64ImageHeight, QImage::Format_RGB888); + } + catch (std::bad_alloc& e) + { + QMessageBox::about(NULL, "Error", "Start Acquisition Failed : Allocate image resources failed! "); + return false; + } + } + + // Clear deque if it is not empty + if (!m_objEmptyBufferDeque.empty()) + { + ClearDeque(); + } + + // Add buffer pointer to empty buffer deque + PushBackToEmptyBufferDeque(m_pImageElement0); + PushBackToEmptyBufferDeque(m_pImageElement1); + PushBackToEmptyBufferDeque(m_pImageElement2); + + return true; +} + + +//---------------------------------------------------------------------------------- +/** +\Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) +\param[in] emStatus Error code +\param[out] +\return void +*/ +//---------------------------------------------------------------------------------- +void CAcquisitionThread::GetAcquistionErrorString(GX_STATUS emError) +{ + char* error_info = NULL; + size_t size = 0; + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Get the length of the error message and alloc memory for error info + emStatus = GXGetLastError(&emError, NULL, &size); + + // Alloc memory for error info + try + { + error_info = new char[size]; + } + catch (std::bad_alloc& e) + { + emit SigAcquisitionError(QString("Alloc error info Faild!")); + return; + } + + // Get the error message and display + emStatus = GXGetLastError (&emError, error_info, &size); + + if (emStatus != GX_STATUS_SUCCESS) + { + emit SigAcquisitionError(QString("Interface of GXGetLastError call failed!")); + } + else + { + emit SigAcquisitionError(QString("%1").arg(QString(QLatin1String(error_info)))); + } + + // Release memory alloced + if (NULL != error_info) + { + delete[] error_info; + error_info = NULL; + } + + return; +} diff --git a/device/AcquisitionThread.h b/device/AcquisitionThread.h new file mode 100644 index 0000000..25a0919 --- /dev/null +++ b/device/AcquisitionThread.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------- +/** +\file AcquisitionThread.h +\brief CAcquisitionThread Class declaration file + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- +#ifndef ACQUISITIONTHREAD_H +#define ACQUISITIONTHREAD_H + +#include +#include +#include + +#include "Common.h" + +/// Image process status +enum PROC_STATUS +{ + PROC_SUCCESS = 0, + PROC_FAIL = -1 +}; + +class CAcquisitionThread : public QThread +{ + Q_OBJECT +public: + explicit CAcquisitionThread(QObject *parent = 0); + ~CAcquisitionThread(); + + /// Get device handle from main-thread + void GetDeviceHandle(GX_DEV_HANDLE); + + /// Alloc QImage resource for show frames on ImageLabel + bool PrepareForShowImg(); + + /// Push back to empty buffer deque + void PushBackToEmptyBufferDeque(QImage*); + + /// Push back to show image deque + void PushBackToShowImageDeque(QImage*); + + /// Pop front from show image deque + QImage* PopFrontFromShowImageDeque(); + + /// Pop front from empty buffer deque + QImage* PopFrontFromEmptyBufferDeque(); + + /// Clear both deque + void ClearDeque(); + + uint64_t m_ui64AcquisitionBufferNum; ///< Acquisition buffer number + uint32_t m_nFrameCount; ///< Acquisition frame count + bool m_bAcquisitionThreadFlag; ///< Acquistion thread run flag + GX_DEV_HANDLE m_hDevice; ///< Device Handle + +private: + /// Process Raw image to RGB image and do image improvment + PROC_STATUS ImageProcess(PGX_FRAME_BUFFER, unsigned char*); + + /// Get Acquisition error and send it to main thread(GXGetLastError can only get string from the thread which error occured) + void GetAcquistionErrorString(GX_STATUS); + + bool m_bSoftTriggerOn; ///< Trigger Mode is on + int64_t m_i64ColorFilter; ///< The bayer format + int64_t m_i64ImageMaxWidth; ///< The Maximum of image width + int64_t m_i64ImageWidth; ///< The image width + int64_t m_i64ImageMaxHeight; ///< The Maximum of image height + int64_t m_i64ImageHeight; ///< The image height + bool m_bColorFilter; ///< Support color pixel format or not + + bool m_bColorCorrection; ///< Flag : ColorCorrection is on or not + bool m_bGammaRegulation; ///< Flag : GammaRegulation is on or not + bool m_bContrastRegulation; ///< Flag : ContrastRegulation is on or not + int64_t m_i64ColorCorrection; ///< Color correction param + unsigned char* m_pGammaLut; ///< Gamma look up table + int m_nGammaLutLength; ///< Gamma look up table length + unsigned char* m_pContrastLut; ///< Contrast look up table + int m_nContrastLutLength; ///< Contrast look up table length + + PGX_FRAME_BUFFER* m_pstarrFrameBuffer; ///< Array of PGX_FRAME_BUFFER + unsigned char* m_pRaw8Image; ///< Intermediate variables between DxRaw16toRaw8 and DxRaw8toRGB24 + QImage* m_pImageElement0; ///< QImage for image showing + QImage* m_pImageElement1; ///< QImage for image showing + QImage* m_pImageElement2; ///< QImage for image showing + + std::deque m_objEmptyBufferDeque; ///< Empty buffer deque + std::deque m_objShowImageDeque; ///< Show image deque + + QMutex m_objParamMutex; ///< Mutex for cross thread parameters + QMutex m_objDequeMutex; ///< Mutex for deque + + +protected: + /// The starting point for acquisition thread. + /// Only code within this function are run in acquisition threads + void run(); + +signals: + /// Acquisition error occured signal + void SigAcquisitionError(QString); + + /// Image process error occured signal + void SigImageProcError(VxInt32); +}; + +#endif // ACQUISITIONTHREAD_H diff --git a/device/Common.h b/device/Common.h new file mode 100644 index 0000000..3d7bf52 --- /dev/null +++ b/device/Common.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------------- +/** +\file Common.h +\brief Common function declare and define macros of GxViewer + +\version v1.0.1807.9271 +\date 2018-07-27 + +

Copyright (c) 2017-2018

+*/ +//---------------------------------------------------------------------------------- + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GxIAPI.h" +#include "DxImageProc.h" + +#define FRAMERATE_INCREMENT 0.1 +#define EXPOSURE_INCREMENT 1 +#define GAIN_INCREMENT 0.1 +#define WHITEBALANCE_DECIMALS 4 +#define WHITEBALANCE_INCREMENT 0.0001 + +/// Release memory allocated +#define RELEASE_ALLOC_MEM(obj) \ + if (obj != NULL) \ + { \ + delete obj; \ + obj = NULL; \ + } + +/// Release memory(array) allocated +#define RELEASE_ALLOC_ARR(obj) \ + if (obj != NULL) \ + { \ + delete[] obj; \ + obj = NULL; \ + } + + +#endif // COMMON_H diff --git a/device/IrisCameraCapEventHandler.cpp b/device/IrisCameraCapEventHandler.cpp new file mode 100644 index 0000000..49e7316 --- /dev/null +++ b/device/IrisCameraCapEventHandler.cpp @@ -0,0 +1,51 @@ +#include "IrisCameraCapEventHandler.h" +#include + +IrisCameraCapEventHandler::IrisCameraCapEventHandler(QObject *parent) : QObject(parent) +{ + +} + +void IrisCameraCapEventHandler::DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam) +{ + if (objImageDataPointer.IsNull() == false) + { + CGXDevicePointer* cam = (CGXDevicePointer *) pUserParam; + + auto camName = cam->getPtr()->GetDeviceInfo().GetUserID(); + int width = objImageDataPointer->GetWidth(); + int height = objImageDataPointer->GetHeight(); + + uchar * pBuffer = (BYTE*)objImageDataPointer->GetBuffer(); + if (pBuffer != NULL) + { + // 相机拍摄下的图像1440*1080, 直接用于算法判断 + QImage img = QImage(pBuffer, width, height, QImage::Format_Grayscale8); // 用于展示 + QImage imgDisp; + + // 图像逆时针旋转90度 + img = img.transformed(QTransform().rotate(90)); + + // 用于显示的图像需要缩小到480 * 640的尺寸 + imgDisp = img.scaled(SettingConfig::getInstance().IRIS_DISPLAY_WIDTH, SettingConfig::getInstance().IRIS_DISPLAY_HEIGHT); + + // 发送到界面进行显示 + emit sendIrisFrameToDraw(imgDisp); + + // 创建虹膜信息对象 + CasicIrisInfo irisInfo; + irisInfo.hasEye = false; + + // 存入图像栈 + cv::Mat irisMat = ImageUtil::QImageToMat(img); + irisInfo.matData = irisMat; + + ProMemory::getInstance().pushCasicIris(irisInfo); + + /* 相机控制只拍图 不找眼 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findEye(irisInfo); + */ + } + } +} diff --git a/device/IrisCameraCapEventHandler.h b/device/IrisCameraCapEventHandler.h new file mode 100644 index 0000000..94f5594 --- /dev/null +++ b/device/IrisCameraCapEventHandler.h @@ -0,0 +1,29 @@ +#ifndef IRISCAMERACAPEVENTHANDLER_H +#define IRISCAMERACAPEVENTHANDLER_H + +#include +#include +#include + +#include "casic/iris/CasicIrisInterface.h" +#include "ProMemory.h" + +#include "utils/UtilInclude.h" + +class IrisCameraCapEventHandler : public QObject, public ICaptureEventHandler +{ + Q_OBJECT +public: + explicit IrisCameraCapEventHandler(QObject *parent = nullptr); + + void DoOnImageCaptured(CImageDataPointer &objImageDataPointer, void *pUserParam); + +private: + unsigned char* pImageBuffer; + +signals: + void sendIrisFrameToDraw(QImage irisImage); + +}; + +#endif // IRISCAMERACAPEVENTHANDLER_H diff --git a/device/IrisCameraController.cpp b/device/IrisCameraController.cpp new file mode 100644 index 0000000..508379f --- /dev/null +++ b/device/IrisCameraController.cpp @@ -0,0 +1,224 @@ +#include "IrisCameraController.h" + +IrisCameraController::IrisCameraController(QObject *parent) : QObject(parent) +{ + initIrisCameraController(); +} +IrisCameraController::~IrisCameraController() +{ + this->closeIrisCamera(); +} + + +void IrisCameraController::initIrisCameraController() +{ + // Init GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXInitLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraInitError(); + LOG_ERROR("[CameraController]初始化虹膜相机失败"); + return ; + } + LOG_INFO("[CameraController]初始化虹膜相机成功"); + + //枚举设备 + UpdateDeviceList(); +} + +void IrisCameraController::openIrisCamera() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + if (m_ui32DeviceNum > 0) { + emStatus = GXOpenDeviceByIndex(1, &m_hDevice); + + if (emStatus == GX_STATUS_SUCCESS) { + LOG_INFO("[CameraController]开启虹膜相机成功"); + + // isOpen flag set true + m_bOpen = true; + + // 使能采集帧率调节模式 + emStatus = GXSetEnum(m_hDevice, GX_ENUM_ACQUISITION_FRAME_RATE_MODE, GX_ACQUISITION_FRAME_RATE_MODE_ON); + + // 设置采集帧率,假设设置为 10.0,用户按照实际需求设置此值 1000 / 100 = 10 + emStatus = GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0); + + SetUpAcquisitionThread(); + } + } + + return; +} + +void IrisCameraController::closeIrisCamera() +{ + // Release GxiApi libary + GX_STATUS emStatus = GX_STATUS_SUCCESS; + emStatus = GXCloseLib(); + if (emStatus != GX_STATUS_SUCCESS) + { + emit irisCameraTermError(); + } +} + +void IrisCameraController::startCapture() +{ + // 获取定时器, 绑定定时函数 +// LOG_DEBUG(QString("[IrisCameraController][startCapture]虹膜相机拍图").toStdString()); + + bool bSetDone = false; + // Set acquisition buffer number + bSetDone = SetAcquisitionBufferNum(); + if (!bSetDone) + { + return; + } + + bool bPrepareDone = false; + // Alloc resource for image acquisition + bPrepareDone = m_pobjAcqThread->PrepareForShowImg(); + if (!bPrepareDone) + { + return; + } + + // Device start acquisition and start acquisition thread + StartAcquisition(); + + LOG_DEBUG(QString("[CameraController][startCapture]虹膜相机拍图").toStdString()); + + // Do not start timer when acquisition start failed + if (!m_bAcquisitionStart) + { + return; + } +} +void IrisCameraController::stopCapture() +{ +// LOG_DEBUG(QString("[IrisCameraController][stopCapture]虹膜相机停止拍图").toStdString()); + this->StopAcquisition(); +} + +void IrisCameraController::SetUpAcquisitionThread() +{ + // if Acquisition thread is on Stop acquisition thread + if (m_pobjAcqThread != NULL) + { + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Release acquisition thread object + RELEASE_ALLOC_MEM(m_pobjAcqThread); + } + + // Instantiation acquisition thread + try + { + m_pobjAcqThread = new CAcquisitionThread(); + m_pobjAcqThread->m_hDevice = m_hDevice; + } + catch (std::bad_alloc &e) + { + QMessageBox::about(NULL, "Allocate memory error", "Cannot allocate memory, please exit this app!"); + RELEASE_ALLOC_MEM(m_pobjAcqThread); + return; + } + + return; +} + +void IrisCameraController::UpdateDeviceList() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + // Enumerate Devcie List + emStatus = GXUpdateDeviceList(&m_ui32DeviceNum, ENUMRATE_TIME_OUT); + + LOG_INFO(QString("[CameraController]枚举虹膜相机成功[%1]").arg(m_ui32DeviceNum).toStdString()); + + return; +} + +void IrisCameraController::StartAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + emStatus = GXStreamOn(m_hDevice); + + // Set acquisition thread run flag + m_pobjAcqThread->m_bAcquisitionThreadFlag = true; + + // Acquisition thread start + m_pobjAcqThread->start(); + + // isStart flag set true + m_bAcquisitionStart = true; + + return; +} + +void IrisCameraController::StopAcquisition() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + + m_pobjAcqThread->m_bAcquisitionThreadFlag = false; + m_pobjAcqThread->quit(); + m_pobjAcqThread->wait(); + + // Turn off stream + emStatus = GXStreamOff(m_hDevice); + + // isStart flag set false + m_bAcquisitionStart = false; + + return; +} + +void IrisCameraController::SetCameraFrameRate(float rate) +{ + GXSetFloat(m_hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, rate); +} + +bool IrisCameraController::SetAcquisitionBufferNum() +{ + GX_STATUS emStatus = GX_STATUS_SUCCESS; + uint64_t ui64BufferNum = 0; + int64_t i64PayloadSize = 0; + + // Get device current payload size + emStatus = GXGetInt(m_hDevice, GX_INT_PAYLOAD_SIZE, &i64PayloadSize); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Set buffer quantity of acquisition queue + if (i64PayloadSize == 0) + { + return false; + } + + // Calculate a reasonable number of Buffers for different payload size + // Small ROI and high frame rate will requires more acquisition Buffer + const size_t MAX_MEMORY_SIZE = 8 * 1024 * 1024; // The maximum number of memory bytes available for allocating frame Buffer + const size_t MIN_BUFFER_NUM = 5; // Minimum frame Buffer number + const size_t MAX_BUFFER_NUM = 450; // Maximum frame Buffer number + ui64BufferNum = MAX_MEMORY_SIZE / i64PayloadSize; + ui64BufferNum = (ui64BufferNum <= MIN_BUFFER_NUM) ? MIN_BUFFER_NUM : ui64BufferNum; + ui64BufferNum = (ui64BufferNum >= MAX_BUFFER_NUM) ? MAX_BUFFER_NUM : ui64BufferNum; + + emStatus = GXSetAcqusitionBufferNumber(m_hDevice, ui64BufferNum); + if (emStatus != GX_STATUS_SUCCESS) + { + return false; + } + + // Transfer buffer number to acquisition thread class for using GXDQAllBufs + m_pobjAcqThread->m_ui64AcquisitionBufferNum = ui64BufferNum; + + return true; +} diff --git a/device/IrisCameraController.h b/device/IrisCameraController.h new file mode 100644 index 0000000..83eb12a --- /dev/null +++ b/device/IrisCameraController.h @@ -0,0 +1,47 @@ +#ifndef IRISCAMERACONTROLLER_H +#define IRISCAMERACONTROLLER_H + +#include + +#include "AcquisitionThread.h" +#include "utils/UtilInclude.h" + +#define ENUMRATE_TIME_OUT 200 + +class IrisCameraController : public QObject +{ + Q_OBJECT +public: + explicit IrisCameraController(QObject *parent = nullptr); + ~IrisCameraController(); + + void openIrisCamera(); + void closeIrisCamera(); + + void startCapture(); + void stopCapture(); + + void SetCameraFrameRate(float rate); + + CAcquisitionThread * m_pobjAcqThread; + GX_DEV_HANDLE m_hDevice; ///< Device Handle + uint32_t m_ui32DeviceNum; ///< Device number enumerated + + bool m_bOpen; ///< Flag : camera is opened or not + bool m_bAcquisitionStart; ///< Flag : camera is acquiring or not + +private: + // 初始化并打开人脸相机 + void initIrisCameraController(); + void SetUpAcquisitionThread(); + void UpdateDeviceList(); + bool SetAcquisitionBufferNum(); + void StartAcquisition(); + void StopAcquisition(); + +signals: + void irisCameraInitError(); + void irisCameraTermError(); +}; + +#endif // IRISCAMERACONTROLLER_H diff --git a/device/device.pri b/device/device.pri new file mode 100644 index 0000000..583721b --- /dev/null +++ b/device/device.pri @@ -0,0 +1,14 @@ + +HEADERS += $$PWD/IrisCameraController.h +#HEADERS += $$PWD/IrisCameraCapEventHandler.h +HEADERS += $$PWD/Common.h +HEADERS += $$PWD/AcquisitionThread.h +HEADERS += $$PWD/iris/IrisRecogProcess.h +HEADERS += $$PWD/iris/CasicIrisRecState.h + +SOURCES += $$PWD/IrisCameraController.cpp +#SOURCES += $$PWD/IrisCameraCapEventHandler.cpp +SOURCES += $$PWD/AcquisitionThread.cpp +SOURCES += $$PWD/iris/IrisRecogProcess.cpp +SOURCES += $$PWD/iris/CasicIrisRecState.cpp + diff --git a/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp new file mode 100644 index 0000000..c5e5f13 --- /dev/null +++ b/device/iris/CasicIrisRecState.cpp @@ -0,0 +1,93 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" +#include "utils/UtilInclude.h" + +CasicIrisRecState::CasicIrisRecState() +{ + this->recoginzeId = "0"; + this->timeStamp = -1; + + this->state = IrisRecStateName::REC_NOT_START; + + this->irisInfo = new CasicIrisInfo(); +} + +CasicIrisRecState::~CasicIrisRecState() +{ + delete this->irisInfo; +} + +void CasicIrisRecState::initRecognize() +{ + QDateTime now = QDateTime::currentDateTime(); + this->recoginzeId = now.toString("yyyyMMddHHmmsszzz"); + this->timeStamp = now.toMSecsSinceEpoch(); + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; + + LOG_INFO(QString("[initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId).toStdString()); + qDebug() << QString("[CasicIrisRecState][initRecognize][%1] 虹膜识别状态初始化").arg(recoginzeId); +} + +void CasicIrisRecState::restRecognize() +{ + this->recoginzeId = ""; + this->timeStamp = 0; + this->timeStampSucc = 0; + + this->tryCount = 0; + this->noEyeCount = 0; + + this->matchedId = ""; + this->score = 0.0f; + this->state = IrisRecStateName::REC_NOT_START; + + this->findEyeTmLast = 0; + this->segmentTmLast = 0; + this->extractTmLast = 0; + this->matchTmLast = 0; + this->recogTimeLast = 0; +} + +QString CasicIrisRecState::toString() +{ + return QString(QJsonDocument(toJSON()).toJson(QJsonDocument::Compact)); +} + + +QJsonObject CasicIrisRecState::toJSON() +{ + QJsonObject obj; + + obj.insert("recoginzeId", recoginzeId); + obj.insert("timestamp", timeStamp); + obj.insert("timestampSucc", timeStampSucc); + + obj.insert("state", state); + obj.insert("matchedId", matchedId); + obj.insert("score", score); + + obj.insert("tryCount", tryCount); + obj.insert("noEyeCount", noEyeCount); + + obj.insert("recogTimeLast", recogTimeLast); + obj.insert("findEyeTmLast", findEyeTmLast); + obj.insert("segmentTmLast", segmentTmLast); + obj.insert("extractTmLast", extractTmLast); + obj.insert("matchTmLast", matchTmLast); + + return obj; +} diff --git a/device/iris/CasicIrisRecState.h b/device/iris/CasicIrisRecState.h new file mode 100644 index 0000000..e978d58 --- /dev/null +++ b/device/iris/CasicIrisRecState.h @@ -0,0 +1,73 @@ +#ifndef CASICIRISRECSTATE_H +#define CASICIRISRECSTATE_H + +#include +#include +#include +#include + +#include "casic/iris/CasicIrisInfo.h" +#include "utils/UtilInclude.h" + +class CasicIrisRecState : public QObject +{ +public: + ~CasicIrisRecState(); + CasicIrisRecState(const CasicIrisRecState&)=delete; + CasicIrisRecState& operator=(const CasicIrisRecState&)=delete; + + static CasicIrisRecState& getInstance() { + static CasicIrisRecState instance; + return instance; + } + + void initRecognize(); + void restRecognize(); + QString toString(); + QJsonObject toJSON(); + + QString recoginzeId; // 识别过程id + qint64 timeStamp = 0; // 识别开始时间戳 + qint64 timeStampSucc = 0; // 识别成功时的时间戳 + qint64 findEyeTmLast = 0; // 找眼操作耗时 + qint64 segmentTmLast = 0; // 图像分割操作耗时 + qint64 extractTmLast = 0; // 编码操作耗时 + qint64 matchTmLast = 0; // 匹配操作耗时 + + float score = 0.0f; // 匹配评分 + + CasicIrisInfo * irisInfo; + + QString matchedId; + + qint8 tryCount = 0; // 识别尝试次数 + qint16 noEyeCount = 0; // 连续没有找到眼睛次数 + qint16 recogTimeLast = 0.0; // 识别成功耗时ms + + /** + * @brief state + * 0=初始值 + * 1=左眼检测 + * 2=右眼检测 + * 3=质量评估 + * 4=特征值提取 + * 5=虹膜库比对成功 + * 6=虹膜库比对失败 + */ + volatile int state; + + enum IrisRecStateName + { + REC_NOT_START = 0, // 未开始 + REC_FIND_EYE = 3, // 找到左眼和右眼 + REC_FEATURE_EXTRACT = 4, // 特征值提取成功 + REC_SEARCH_SUCC = 5, // 虹膜库比对成功 + REC_SEARCH_FAIL = 6 // 虹膜库比对失败 + }; + +private: + CasicIrisRecState(); + +}; + +#endif // CASICIRISRECSTATE_H diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp new file mode 100644 index 0000000..54d38b5 --- /dev/null +++ b/device/iris/IrisRecogProcess.cpp @@ -0,0 +1,348 @@ +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "IrisRecogProcess.h" + +IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent) +{ + this->working = false; + this->exit = false; + + // 连接算法服务 + clientUtil = new SocketClientUtil(this); + clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007); + + // 调用socket发送消息 + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); +} + +IrisRecogProcess::~IrisRecogProcess() +{ + this->setWorking(false); +} + +void IrisRecogProcess::setWorking(bool working) +{ + this->working = working; +} + +void IrisRecogProcess::exitThread() +{ + this->exit = true; +} + +void IrisRecogProcess::run() +{ + while (exit == false) + { + // 如果工作标志位为否, 则跳出 + if (this->working == false) + { + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束 + if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START) + { + qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 2. 图像栈中没有图像则直接返回 + if (ProMemory::getInstance().isIrisQueueEmpty() == true) + { + qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms"); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 3. 取出虹膜图像栈中的一条数据 + CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); + + QElapsedTimer timer; + timer.start(); + + // 4. 调用找眼分类器算法 第一阶段 + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE); + irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); + + // 找眼计时 + int findEyeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast); + +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; + + // 4.1 没有找到眼睛则返回 + if (irisInfo.hasEye == false) + { + // 表示本次识别结束, 可以开始下一次识别过程 + qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount); + + addOneNoEyeCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 5. 找到眼睛则开始识别 + + // 发送信号更新界面 + emit startIdentifyIris(); + + // 调用远程算法进行编码 + if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "") + { + // 第一次找到眼则 虹膜识别状态初始化 + CasicIrisRecState::getInstance().initRecognize(); + } + + CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; + + LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString()); + CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast; + CasicIrisRecState::getInstance().recogTimeLast += findEyeLast; + + // 6. 分割图像 + timer.restart(); + + // 进行预处理 转换成 320 * 240 的三通道图像 送去分割 + cv::Mat segMat; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5)); + + // save segMat for debug + std::string segFilename; + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(segFilename, segMat); + } + +// QElapsedTimer timer; +// timer.start(); +// CasicIrisInfo irisInfo; +// irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0); +// irisInfo.hasEye = true; +// cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp"); + + int segMatSize = segMat.cols * segMat.rows; + QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道 + + // 发送TCP消息去匹配 + emit sendDataToExract(data); + + // 等待接收算法返回的结果 + QTimer toTimer; + QEventLoop loop; + toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待 + connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); + loop.exec(); + + if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) { + addOneTryCount(); + qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed()); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + char recvPupil[320 * 240 * 1]; + char recvMask[320 * 240 * 1]; + char recvIris[320 * 240 * 1]; + memset(recvPupil, 0, sizeof(recvPupil)); + memset(recvMask, 0, sizeof (recvMask)); + memset(recvIris, 0, sizeof (recvIris)); + for (int i = 0; i < clientUtil->getResponse().size(); i++) { + if (i < segMatSize) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= segMatSize && i < 2 * segMatSize) + recvMask[i - segMatSize] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil); + cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask); + cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; + + // 成功接收后清除缓存区 + clientUtil->resetRecvBuffer(); +/* + std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + // 第二阶段 分割图像计时 + int segLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast); + LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString()); + CasicIrisRecState::getInstance().segmentTmLast = segLast; + CasicIrisRecState::getInstance().recogTimeLast += segLast; + + // 7. 分割后的后处理 编码及提取特征值 + timer.restart(); + if (cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 状态修改为特征提取 + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + + if (irisInfo.postProcSucc == false) { + qDebug() << "算法post process失败,暂停200ms"; + + addOneTryCount(); + this->msleep(THREAD_MSLEEP); // 200ms后再判断 + continue; + } + + // 从编码中提取特征值 + irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo); + + int encodeLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast); + LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString()); + CasicIrisRecState::getInstance().extractTmLast = encodeLast; + CasicIrisRecState::getInstance().recogTimeLast += encodeLast; + +// cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData); + + // 开始匹配 + timer.restart(); + float minScore = 1.0f; + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + + float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值 + if (score < minScore) { + minScore = score; + } + + if (score <= 0.32) { + CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC; + CasicIrisRecState::getInstance().matchedId = feature.personId; + CasicIrisRecState::getInstance().score = score; + CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch(); + + break ; + } + } + + int matchLast = timer.elapsed(); + qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state); + LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString()); + CasicIrisRecState::getInstance().matchTmLast = matchLast; + CasicIrisRecState::getInstance().recogTimeLast += matchLast; + + if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) { + QFile::remove(QString::fromStdString(segFilename)); // delete segment file + cv::imwrite(QString("%1/%2/%3-%4.bmp") + .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH) + .arg(QDate::currentDate().toString("yyyyMMdd")) + .arg(QTime::currentTime().toString("HHmmss.zzz")) + .arg(minScore).toStdString(), irisInfo.matData); + } + + if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) { + // 找到匹配结果 + afterRecogAction(); + + emit findMatchedIris(CasicIrisRecState::getInstance().matchedId); + } else { + // 没有匹配上 + addOneTryCount(); + } + } +} + +void IrisRecogProcess::afterRecogAction() +{ + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈 +} + +void IrisRecogProcess::addOneTryCount() +{ + CasicIrisRecState::getInstance().tryCount++; +// LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString(); +// LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString()); + + CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START; + if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT) + { + CasicIrisRecState::getInstance().noEyeCount = 0; + CasicIrisRecState::getInstance().tryCount = 0; + + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + + emit failedMatchedIris(); + } +} +void IrisRecogProcess::addOneNoEyeCount() +{ + if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在识别中 则超过一定次数后返回识别失败 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT) + { + // 发送信号识别失败 + emit failedMatchedIris(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图 + this->setWorking(false); + ProMemory::getInstance().irisCamCtrl->stopCapture(); + ProMemory::getInstance().clearIrisQueue(); + } + } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING) + { + CasicIrisRecState::getInstance().noEyeCount++; + + // 如果在工作状态 则超过一定次数后返回待机 + if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT && + SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0) + { + // 发送信号待机 + emit backToLockScreen(); + + CasicIrisRecState::getInstance().noEyeCount = 0; + + // 待机界面还在拍图和工作 + ProMemory::getInstance().clearIrisQueue(); + } + } else { + // 待机界面 不做处理 等待找到眼 + } +} diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h new file mode 100644 index 0000000..e21c1a7 --- /dev/null +++ b/device/iris/IrisRecogProcess.h @@ -0,0 +1,52 @@ +#ifndef IRISRECOGPROCESS_H +#define IRISRECOGPROCESS_H + +#define THREAD_MSLEEP 200 + +#include +#include +#include +#include + +#include "ProMemory.h" +#include "device/iris/CasicIrisRecState.h" +#include "casic/iris/CasicIrisInterface.h" + +class IrisRecogProcess : public QThread +{ + Q_OBJECT +public: + explicit IrisRecogProcess(QObject *parent = 0); + ~IrisRecogProcess(); + + void setWorking(bool working); + void exitThread(); + + void addOneTryCount(); + void addOneNoEyeCount(); + +protected: + //QThread的虚函数 + //线程处理函数 + //不能直接调用, 通过start()间接调用 + void run(); + + volatile bool working; + volatile bool exit; + +private: + SocketClientUtil * clientUtil; + + void afterRecogAction(); + +signals: + void sendDataToExract(QByteArray data); + + void backToLockScreen(); + void startIdentifyIris(); + void failedMatchedIris(); + void findMatchedIris(QString personId); + +}; + +#endif // IRISRECOGPROCESS_H diff --git a/images/bg_failure.png b/images/bg_failure.png new file mode 100644 index 0000000..55000a0 --- /dev/null +++ b/images/bg_failure.png Binary files differ diff --git a/images/bg_footer.png b/images/bg_footer.png new file mode 100644 index 0000000..a3a99ab --- /dev/null +++ b/images/bg_footer.png Binary files differ diff --git a/images/bg_identifying.png b/images/bg_identifying.png new file mode 100644 index 0000000..58d5a95 --- /dev/null +++ b/images/bg_identifying.png Binary files differ diff --git a/images/bg_statcked.png b/images/bg_statcked.png new file mode 100644 index 0000000..c76130f --- /dev/null +++ b/images/bg_statcked.png Binary files differ diff --git a/images/bg_success.png b/images/bg_success.png new file mode 100644 index 0000000..c845bb8 --- /dev/null +++ b/images/bg_success.png Binary files differ diff --git a/images/bg_title.png b/images/bg_title.png new file mode 100644 index 0000000..131369a --- /dev/null +++ b/images/bg_title.png Binary files differ diff --git a/images/iconWarn.png b/images/iconWarn.png new file mode 100644 index 0000000..e1d6403 --- /dev/null +++ b/images/iconWarn.png Binary files differ diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000..6f8e677 --- /dev/null +++ b/images/lock.png Binary files differ diff --git a/images/photo.png b/images/photo.png new file mode 100644 index 0000000..d9fa9fa --- /dev/null +++ b/images/photo.png Binary files differ diff --git a/main.cpp b/main.cpp index 16bc45b..3bfb1c6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "IdentifyForm.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - IdentifyForm w; - w.show(); - return a.exec(); -} +#include "MainWindowForm.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindowForm w; + w.show(); + return a.exec(); +} diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..0931777 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,13 @@ + + + images/bg_title.png + images/bg_footer.png + images/bg_statcked.png + images/bg_identifying.png + images/bg_failure.png + images/iconWarn.png + images/photo.png + images/bg_success.png + images/lock.png + + diff --git a/utils/ByteUtil.cpp b/utils/ByteUtil.cpp new file mode 100644 index 0000000..45d392e --- /dev/null +++ b/utils/ByteUtil.cpp @@ -0,0 +1,178 @@ +#include "ByteUtil.h" +#include + +static uchar uc = 0x00; +const int FLOAT_BYTE_LENGTH = 4; +const int DOUBLE_BYTE_LENGTH = 8; + +ByteUtil::ByteUtil(QObject *parent) : QObject(parent) +{ + +} + +QByteArray ByteUtil::appendZeroAlign(QByteArray array, int count) +{ + // 如果字节数组的长度不足cout的倍数,在字节数组的前段补0x00 + int rem = array.length() % count; + if (rem > 0) + { + array.insert(0, count - rem, uc); + } + + return array; +} + +QString ByteUtil::binToHexString(QByteArray bytes) +{ + return bytes.toHex().toUpper(); +} + +QByteArray ByteUtil::hexStringToBytes(QString hexString) +{ + hexString = hexString.toUpper(); + if (hexString.length() % 2 == 1) + { + hexString = "0" + hexString; + } + + bool ok; + QByteArray bytes; + for (int i = 0; i < hexString.length() - 1; i = i + 2) + { + QString str = hexString.mid(i, 2); + bytes.append(str.toInt(&ok, 16)); + } + + return bytes; +} + + +qulonglong ByteUtil::binToULong(QByteArray bytes, quint8 length) +{ + qulonglong value = 0; + + for (int i = 0; i < bytes.length() && i < length; i++) + { + value = value * 256 + (quint8) bytes.at(i); + } + + return value; +} + + +float ByteUtil::binToFloat(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // 如果字节数组长度超过4位,取前4位 + QString str = ByteUtil::binToHexString(bytes.mid(0, FLOAT_BYTE_LENGTH)); + int hex = str.toUInt(0, 16); + float ret = *(float*) &hex; + + return ret; +} + +QVector ByteUtil::binToFloatArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, FLOAT_BYTE_LENGTH); + + // float用4字节浮点数表示 + int length = bytes.length() / FLOAT_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 4个字节的浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * FLOAT_BYTE_LENGTH, FLOAT_BYTE_LENGTH)); + + int hex = str.toUInt(0, 16); + float fl = *(float*) &hex; + + ret.append(fl); + } + + return ret; +} + +QByteArray ByteUtil::floatToBytes(float value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < FLOAT_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::floatArrayToBytes(QVector floatArray) +{ + QByteArray ret; + for (int i = 0; i < floatArray.length(); i++) + { + ret.append(floatToBytes(floatArray.at(i))); + } + return ret; +} + + +double ByteUtil::binToDouble(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // 如果字节数组长度超过8位,取前8位 + QString str = ByteUtil::binToHexString(bytes.mid(0, DOUBLE_BYTE_LENGTH)); + qulonglong hex = str.toULongLong(0, 16); + double ret = *(double*) &hex; + + return ret; +} + +QVector ByteUtil::binToDoubleArray(QByteArray bytes) +{ + bytes = appendZeroAlign(bytes, DOUBLE_BYTE_LENGTH); + + // double用8字节浮点数表示 + int length = bytes.length() / DOUBLE_BYTE_LENGTH; + + // 返回值 + QVector ret; + + // 8个字节的双精度浮点数,转换为 + for (int i = 0; i < length; i++) + { + QString str = ByteUtil::binToHexString(bytes.mid(i * DOUBLE_BYTE_LENGTH, DOUBLE_BYTE_LENGTH)); + + qulonglong hex = str.toULongLong(0, 16); + double dl = *(double*) &hex; + + ret.append(dl); + } + + return ret; +} + +QByteArray ByteUtil::doubleToBytes(double value) +{ + uchar * hex = (uchar *) &value; + QByteArray ret; + for (int i = 0; i < DOUBLE_BYTE_LENGTH; i++) + { + ret.insert(0, hex[i]); + } + + return ret; +} + +QByteArray ByteUtil::doubleArrayToBytes(QVector doubleArray) +{ + QByteArray ret; + for (int i = 0; i < doubleArray.length(); i++) + { + ret.append(doubleToBytes(doubleArray.at(i))); + } + return ret; +} diff --git a/utils/ByteUtil.h b/utils/ByteUtil.h new file mode 100644 index 0000000..7386810 --- /dev/null +++ b/utils/ByteUtil.h @@ -0,0 +1,115 @@ +#ifndef BYTEUTIL_H +#define BYTEUTIL_H + +#include + +class ByteUtil : public QObject +{ + Q_OBJECT +public: + explicit ByteUtil(QObject *parent = nullptr); + + + /******** 字节数组与字符串互转 ********/ + /** + * @brief binToHexString + * @param bytes + * @note 16进制字节数组转字符串,用于输出显示,不含空格 + * @return + */ + static QString binToHexString(QByteArray bytes); + /** + * @brief hexStringToBytes + * @param hexString + * @note 16进制字符串转字节数组,不含空格 + * @return + */ + static QByteArray hexStringToBytes(QString hexString); + + /******** 字节数组与long互转 ********/ + /** + * @brief binToULong + * @param bytes + * @note 16进制字节数组转无符号long,length个字节。字符串顺序,高位在前,低位在后 + * 如0x0080000000086A43 = 36028797019515459 + * @return + */ + static qulonglong binToULong(QByteArray bytes, quint8 length); + static QByteArray ULongToBytes(qulonglong value); + + /******** 字节数组与float互转 ********/ + /** + * @brief binToFloat + * @param bytes + * @note 4个字节转float浮点数,从左到右排序 + * @example {0x42, 0xF6, 0xE6, 0x66} 转换为 123.45 + * @return + */ + static float binToFloat(QByteArray bytes); + /** + * @brief binToFloatArray + * @param bytes + * @note 字节数组转float数组,16进制浮点数,4个字节表示1个float浮点数,从左到右排序 + * @example {0xBD, 0x1F, 0xB1, 0xDA, 0x40, 0x63, 0x02, 0x0C, 0x40, 0x49, 0x0F, 0xDA} 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToFloatArray(QByteArray bytes); + /** + * @brief floatToBytes + * @param value + * @note 单个float数转4字节数组,从左至右排序 + * @example 123.45 转换为 {0x42, 0xF6, 0xE6, 0x66} + * @return + */ + static QByteArray floatToBytes(float value); + /** + * @brief floatArrayToBytes + * @param floatArray + * @note float数组转换为字节数组,每一个float浮点数4字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 {0x3D, 0x79, 0x09, 0x6C, 0x40, 0x12, 0x7C, 0x5B, 0x3B, 0xDE, 0x9A, 0x3F} + * @return + */ + static QByteArray floatArrayToBytes(QVector floatArray); + + /******** 字节数组与double互转 ********/ + /** + * @brief binToDouble + * @param bytes + * @note 8个字节转double双精度浮点数,从左到右排序 + * @example C00921FB53D92715 转换为 -3.141592650475 + * @return + */ + static double binToDouble(QByteArray bytes); + /** + * @brief binToDoubleArray + * @param bytes + * @note 字节数组转double数组,16进制双精度浮点数,8个字节表示1个double浮点数,从左到右排序 + * @example BFA3F63C31DF761D400C604189374BC74039B2DE00D1B717 转换为 -0.038988, 3.547, 3.141593 + * @return + */ + static QVector binToDoubleArray(QByteArray bytes); + /** + * @brief doubleToBytes + * @param value + * @note 单个double数转换为8字节数组,从左到右排序 + * @example 123.45 转换为 405EDCCCCCCCCCCD + * @return + */ + static QByteArray doubleToBytes(double value); + /** + * @brief doubleArrayToBytes + * @param doubleArray + * @note double数组转换为字节数组,每个double双精度浮点数8字节,从左到右排序 + * @example 0.0608, 2.28884, 0.00679329 转换为 3FAF212D77318FC540024F8B588E368F3F7BD347E61DABB7 + * @return + */ + static QByteArray doubleArrayToBytes(QVector doubleArray); + +private: + static QByteArray appendZeroAlign(QByteArray array, int count); + +signals: + +}; + +#endif // BYTEUTIL_H diff --git a/utils/ImageUtil.cpp b/utils/ImageUtil.cpp new file mode 100644 index 0000000..d40ea79 --- /dev/null +++ b/utils/ImageUtil.cpp @@ -0,0 +1,107 @@ +#include "ImageUtil.h" + +ImageUtil::ImageUtil() +{ + +} + +QImage ImageUtil::MatImageToQImage(const cv::Mat &src) +{ + //CV_8UC1 8位无符号的单通道---灰度图片 + if(src.type() == CV_8UC1) + { + QImage qImage((const unsigned char *)(src.data), src.cols, src.rows, src.cols, QImage::Format_Grayscale8); + return qImage; + } + //为3通道的彩色图片 + else if(src.type() == CV_8UC3) + { + //得到图像的的首地址 + const uchar *pSrc = (const uchar*)src.data; + //以src构造图片 + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); + } + //四通道图片, 带Alpha通道的RGB彩色图像 + else if(src.type() == CV_8UC4) + { + const uchar *pSrc = (const uchar*)src.data; + QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); + //返回图像的子区域作为一个新图像 + return qImage.copy(); + } + else + { + return QImage(); + } +} + +cv::Mat ImageUtil::ucharToMat(unsigned char *data, int row, int col) +{ + cv::Mat img(row, col, CV_8UC1, (unsigned char *)data); + return img; +} + +cv::Mat ImageUtil::QImageToMat(QImage image) +{ + cv::Mat mat; + switch(image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_Grayscale8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine()); + break; + + default: + break; + } + + mat = mat.clone(); + + return mat; +} + +/* +QImage ImageUtil::UcharToQImage(unsigned char* data, int width, int height, QImage::Format format) +{ + QImage qImage(data, width, height, format); + //在不改变实际图像数据的条件下, 交换红蓝通道 + return qImage.rgbSwapped(); +} + +QString ImageUtil::QImageToBase64(QImage image) +{ + QByteArray ba; + QBuffer buf(&ba); + image.save(&buf, "bmp"); + QString base64String = ba.toBase64(); + return base64String; +} + +cv::Mat ImageUtil::MatImageRect(const cv::Mat &src, cv::Rect rect, int delta) +{ + cv::Mat matClone = src.clone(); + cv::Rect bigRect; + + bigRect.x = rect.x - delta < 0 ? 0 : rect.x - delta; + bigRect.y = rect.y - delta < 0 ? 0 : rect.y - delta; + bigRect.width = rect.x + rect.width + 2 * delta > src.cols ? src.cols - rect.x : rect.width + 2 * delta; + bigRect.height = rect.y + rect.height + 2 * delta > src.rows ? src.rows - rect.y : rect.height + 2 * delta; + + cv::Mat roi = matClone(bigRect); + + return roi; +} +*/ diff --git a/utils/ImageUtil.h b/utils/ImageUtil.h new file mode 100644 index 0000000..4b702f4 --- /dev/null +++ b/utils/ImageUtil.h @@ -0,0 +1,23 @@ +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include "opencv2/opencv.hpp" + +class ImageUtil +{ +public: + ImageUtil(); + + static QImage MatImageToQImage(const cv::Mat &src); +// static QImage UcharToQImage(unsigned char* data, int width, int height, QImage::Format format); + + static cv::Mat ucharToMat(unsigned char * data, int row, int col); + static cv::Mat QImageToMat(QImage image); +// static QString QImageToBase64(QImage image); + +// static cv::Mat MatImageRect(const cv::Mat &src, cv::Rect rect, int delta); +}; + +#endif // IMAGEUTIL_H diff --git a/utils/LogUtil.h b/utils/LogUtil.h new file mode 100644 index 0000000..eb4147c --- /dev/null +++ b/utils/LogUtil.h @@ -0,0 +1,87 @@ +#ifndef LOGUTIL_H +#define LOGUTIL_H + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/daily_file_sink.h" + +#include "SettingConfig.h" + +// use embedded macro to support file and line number +#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__) +#define LOG_INFO(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__) +#define LOG_WARN(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(LogUtil::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__) + + +class LogUtil +{ +public: + ~LogUtil() + { + spdlog::drop_all(); + } + + LogUtil(const LogUtil&)=delete; + LogUtil& operator=(const LogUtil&)=delete; //禁止生成默认赋值函数 + + // magic singleton static + static LogUtil* getInstance() { + static LogUtil instance; + return &instance; + } + + std::shared_ptr getLogger() + { + return m_logger; + } + +private: + LogUtil() + { + try { + + // Create a daily logger - a new file is created every day on 00:00am + m_logger = spdlog::daily_logger_mt("CasicIrisIdentify", SettingConfig::getInstance().LOG_FILE.toStdString(), 0, 0); + + m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l] %v"); + + std::string level = SettingConfig::getInstance().LOG_LEVEL.toStdString(); + if (level == "trace") + { + m_logger->set_level(spdlog::level::trace); + m_logger->flush_on(spdlog::level::trace); + } + else if (level == "debug") + { + m_logger->set_level(spdlog::level::debug); + m_logger->flush_on(spdlog::level::debug); + } + else if (level == "info") + { + m_logger->set_level(spdlog::level::info); + m_logger->flush_on(spdlog::level::info); + } + else if (level == "warn") + { + m_logger->set_level(spdlog::level::warn); + m_logger->flush_on(spdlog::level::warn); + } + else if (level == "error") + { + m_logger->set_level(spdlog::level::err); + m_logger->flush_on(spdlog::level::err); + } + } + catch (const spdlog::spdlog_ex& ex) + { + spdlog::error("init log failed: {}",ex.what()); + + } + } + + std::shared_ptr m_logger; + +}; + +#endif // LOGUTIL_H diff --git a/utils/SettingConfig.cpp b/utils/SettingConfig.cpp new file mode 100644 index 0000000..17abe3e --- /dev/null +++ b/utils/SettingConfig.cpp @@ -0,0 +1,48 @@ +#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(); + + DEBUG_CAMERA_ROTATE = getProperty("debug", "cameraRotate").toString(); + DEBUG_SEGMENT_IP = getProperty("debug", "segmentIp").toString(); + DEBUG_SAVE_IMAGE = getProperty("debug", "saveImage").toBool(); + DEBUG_IMAGE_BASE_PATH = getProperty("debug", "imageBasePath").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..6909e84 --- /dev/null +++ b/utils/SettingConfig.h @@ -0,0 +1,70 @@ +#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; + + QString DEBUG_CAMERA_ROTATE; + QString DEBUG_SEGMENT_IP; + bool DEBUG_SAVE_IMAGE; + QString DEBUG_IMAGE_BASE_PATH; + +private: + SettingConfig(); + + QString filename; + QSettings* setting; +}; + +#endif // SETTINGCONFIG_H diff --git a/utils/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..20b8869 --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,58 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::connect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + QObject::disconnect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); + QObject::disconnect(&objClient, &QTcpSocket::disconnected, this, &SocketClientUtil::socketReconnect); + + objClient.disconnectFromHost(); + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::resetRecvBuffer() +{ + response.clear(); + response.resize(0); +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} + +void SocketClientUtil::socketReconnect() +{ + objClient.close(); + objClient.connectToHost(this->host, this->port); +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..8bd1b8c --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,37 @@ +#ifndef SOCKETCLIENTUTIL_H +#define SOCKETCLIENTUTIL_H + +#include +#include +#include + +class SocketClientUtil : public QObject +{ + Q_OBJECT +public: + explicit SocketClientUtil(QObject *parent = nullptr); + ~SocketClientUtil() {}; + + void connect(QString host, int port); + void closeConnect(); + QByteArray getResponse(); + void resetRecvBuffer(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + void socketReconnect(); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/SpeakerUtil.cpp b/utils/SpeakerUtil.cpp new file mode 100644 index 0000000..933809c --- /dev/null +++ b/utils/SpeakerUtil.cpp @@ -0,0 +1,35 @@ +#include "SpeakerUtil.h" + +SpeakerUtil::SpeakerUtil(QObject *parent) : QObject(parent) +{ + tts = new QTextToSpeech(parent); + tts->setLocale(QLocale::Chinese); + tts->setRate(0.0); + tts->setPitch(1.0); + tts->setVolume(1.0); + + // 循环播放 + effect.setLoopCount(QSoundEffect::Null); + // 设置音量,0-1 + effect.setVolume(1.0f); +} + +void SpeakerUtil::speak(QString content) +{ + if (tts->state() == QTextToSpeech::Ready && tts->state() != QTextToSpeech::Speaking) + { + tts->say(content); + } +} + +void SpeakerUtil::sayIdentifySuccessZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recSuccess.wav")); + effect.play(); +} + +void SpeakerUtil::sayIdentifyFailureZhCn() +{ + effect.setSource(QUrl::fromLocalFile("wav/recFailureRetry.wav")); + effect.play(); +} diff --git a/utils/SpeakerUtil.h b/utils/SpeakerUtil.h new file mode 100644 index 0000000..02b221b --- /dev/null +++ b/utils/SpeakerUtil.h @@ -0,0 +1,35 @@ +#ifndef SPEAKERUTIL_H +#define SPEAKERUTIL_H + +#include +#include +#include + +class SpeakerUtil : public QObject +{ + Q_OBJECT +public: + ~SpeakerUtil() {} + SpeakerUtil(const SpeakerUtil&)=delete; + SpeakerUtil& operator=(const SpeakerUtil&)=delete; + + static SpeakerUtil& getInstance() { + static SpeakerUtil instance; + return instance; + } + + void speak(QString content); + void sayIdentifySuccessZhCn(); + void sayIdentifyFailureZhCn(); + +private: + explicit SpeakerUtil(QObject *parent = nullptr); + + QTextToSpeech * tts; + QSoundEffect effect; + +signals: + +}; + +#endif // SPEAKERUTIL_H diff --git a/utils/TimeCounterUtil.cpp b/utils/TimeCounterUtil.cpp new file mode 100644 index 0000000..78c3d69 --- /dev/null +++ b/utils/TimeCounterUtil.cpp @@ -0,0 +1,9 @@ +#include "TimeCounterUtil.h" + +TimeCounterUtil::TimeCounterUtil() +{ + faceCapCounter = new QTimer(); + irisCapCounter = new QTimer(); + clockCounter = new QTimer(); + videoCounter = new QTimer(); +} diff --git a/utils/TimeCounterUtil.h b/utils/TimeCounterUtil.h new file mode 100644 index 0000000..91f8da0 --- /dev/null +++ b/utils/TimeCounterUtil.h @@ -0,0 +1,30 @@ +#ifndef TIMECOUNTERUTIL_H +#define TIMECOUNTERUTIL_H + +#include +#include + +class TimeCounterUtil : public QObject +{ +public: + + ~TimeCounterUtil() {}; + TimeCounterUtil(const TimeCounterUtil&)=delete; + TimeCounterUtil& operator=(const TimeCounterUtil&)=delete; + + static TimeCounterUtil& getInstance() { + static TimeCounterUtil instance; + return instance; + } + + QTimer * faceCapCounter; + QTimer * irisCapCounter; + QTimer * clockCounter; + QTimer * videoCounter; // show video images on main window + +private: + TimeCounterUtil(); + +}; + +#endif // TIMECOUNTERUTIL_H diff --git a/utils/UtilInclude.h b/utils/UtilInclude.h new file mode 100644 index 0000000..bea17be --- /dev/null +++ b/utils/UtilInclude.h @@ -0,0 +1,15 @@ +#ifndef UTILINCLUDE_H +#define UTILINCLUDE_H + +#include +#include "ByteUtil.h" +#include "ImageUtil.h" +#include "LogUtil.h" +#include "SettingConfig.h" +#include "SocketClientUtil.h" +#include "SpeakerUtil.h" +#include "TimeCounterUtil.h" +//#include "UDPClientUtil.h" +//#include "HttpRequestUtil.h" + +#endif // UTILINCLUDE_H diff --git a/utils/id/IdWorker.h b/utils/id/IdWorker.h new file mode 100644 index 0000000..079bbcb --- /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..4fc555a --- /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..daad45c --- /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..4b2967f --- /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..bc84cae --- /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 +