diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + QByteArray irisFeatureCode; +}; + +struct CasicIrisFeature +{ + int irisId; + QString personId; + cv::Mat irisCode; + cv::Mat maskNorm; + QByteArray irisFeatureCode; }; #endif // CASICIRISINFO_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // end of class CasicIrisRec + +} // end of namespace iristrt + +#endif diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri index d0942ce..43a1bd3 100644 --- a/casic/iris/casicIris.pri +++ b/casic/iris/casicIris.pri @@ -1,4 +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/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri index d0942ce..43a1bd3 100644 --- a/casic/iris/casicIris.pri +++ b/casic/iris/casicIris.pri @@ -1,4 +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/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp index b64d747..6d2553e 100644 --- a/device/iris/CasicIrisRecState.cpp +++ b/device/iris/CasicIrisRecState.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisRecState.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" #include "utils/UtilInclude.h" CasicIrisRecState::CasicIrisRecState() @@ -50,14 +54,14 @@ QJsonObject eyeObj; eyeObj.insert("hasEye", irisInfo->hasEye); - QJsonArray rectArray; - rectArray.append(irisInfo->eyeRect.x); - rectArray.append(irisInfo->eyeRect.y); - rectArray.append(irisInfo->eyeRect.width); - rectArray.append(irisInfo->eyeRect.height); - eyeObj.insert("faceRect", rectArray); +// QJsonArray rectArray; +// rectArray.append(irisInfo->eyeRect.x); +// rectArray.append(irisInfo->eyeRect.y); +// rectArray.append(irisInfo->eyeRect.width); +// rectArray.append(irisInfo->eyeRect.height); +// eyeObj.insert("faceRect", rectArray); - obj.insert("eye", eyeObj); +// obj.insert("eye", eyeObj); return obj; } diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri index d0942ce..43a1bd3 100644 --- a/casic/iris/casicIris.pri +++ b/casic/iris/casicIris.pri @@ -1,4 +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/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp index b64d747..6d2553e 100644 --- a/device/iris/CasicIrisRecState.cpp +++ b/device/iris/CasicIrisRecState.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisRecState.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" #include "utils/UtilInclude.h" CasicIrisRecState::CasicIrisRecState() @@ -50,14 +54,14 @@ QJsonObject eyeObj; eyeObj.insert("hasEye", irisInfo->hasEye); - QJsonArray rectArray; - rectArray.append(irisInfo->eyeRect.x); - rectArray.append(irisInfo->eyeRect.y); - rectArray.append(irisInfo->eyeRect.width); - rectArray.append(irisInfo->eyeRect.height); - eyeObj.insert("faceRect", rectArray); +// QJsonArray rectArray; +// rectArray.append(irisInfo->eyeRect.x); +// rectArray.append(irisInfo->eyeRect.y); +// rectArray.append(irisInfo->eyeRect.width); +// rectArray.append(irisInfo->eyeRect.height); +// eyeObj.insert("faceRect", rectArray); - obj.insert("eye", eyeObj); +// obj.insert("eye", eyeObj); return obj; } diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp index 6869ece..08c8227 100644 --- a/device/iris/IrisRecogProcess.cpp +++ b/device/iris/IrisRecogProcess.cpp @@ -11,11 +11,11 @@ this->exit = false; // 连接算法服务 -// clientUtil = new SocketClientUtil(this); -// clientUtil->connect("127.0.0.1", 2015); + clientUtil = new SocketClientUtil(this); + clientUtil->connect("192.168.83.223", 50007); // 调用socket发送消息 -// connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); } IrisRecogProcess::~IrisRecogProcess() @@ -64,9 +64,6 @@ continue; } -// LOG(TRACE) << QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString(); -// LOG_TRACE(QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString()); - // 取出虹膜图像栈中的一条数据 CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); @@ -75,7 +72,7 @@ qDebug() << QString("[IrisRecogProcess] 取出虹膜图像"); // 调用找眼分类器算法 - casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(200); irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); // 没有找到眼睛则返回 @@ -104,34 +101,107 @@ CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; - CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; - CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; - CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; - CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); +// CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; +// CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; +// CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; +// CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + +// irisInfo.matData = cv::imread("d:\\irisLogs\\photo\\left.bmp", 0); QElapsedTimer timer; timer.start(); - QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); - data.prepend("[-m]"); // 识别 + // 进行预处理 如图像 + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisPreProcess(irisInfo); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 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(); + + 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 < 320 * 240) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= 320 * 240 && i < 2 * 320 * 240) + recvMask[i - 320 * 240] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * 320 * 240] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(240, 320, CV_8UC1, recvPupil); + cv::Mat mask(240, 320, CV_8UC1, recvMask); + cv::Mat iris(240, 320, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; +/* + std::string pupilSeg = QString("%1\\%2\\%3-pupil-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1\\%2\\%3-mask-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1\\%2\\%3-iris-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + qDebug() << QString("算法分割图像成功[%1 ms]").arg(timer.elapsed()); + + // 分割后的后处理 + if(cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0|| cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(200); // 200ms后再判断 + continue; + } + // 状态修改为特征提取 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; - QTimer toTimer; - QEventLoop loop; - toTimer.singleShot(3000, &loop, &QEventLoop::quit); -// connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); - loop.exec(); + timer.restart(); + qDebug() << "开始提取特征"; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1\\%2\\%3-code.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskFilename = QString("%1\\%2\\%3-mask.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + qDebug() << QString("虹膜图像编码成功[%1 ms]").arg(timer.elapsed()); -// QByteArray matchResponse = clientUtil->getResponse(); - QByteArray matchResponse; + // 开始匹配 + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + float score = casic::iris::CasicIrisInterface::getInstance().calculateMatchScore(feature, irisInfo); + qDebug() << score; + if (score <= 0.32) { + // 找到匹配结果 + emit findMatchedIris(feature.personId); + + break; + } + } + + // 没有匹配上 + addOneTryCount(); + +// QByteArray matchResponse; // LOG(INFO) << QString("算法匹配结果").toStdString() << matchResponse.toStdString(); // LOG_INFO("算法匹配结果 {}",matchResponse.toStdString()); - +/* // 算法调用返回成功 if (matchResponse.size() > 0) { @@ -172,7 +242,7 @@ { // 超时返回则尝试次数+1 addOneTryCount(); - } + }*/ } } @@ -213,11 +283,11 @@ // ProMemory::getInstance().irisCam->stopCapture(); ProMemory::getInstance().clearIrisQueue(); - if (qrand() % 10 < 5) { +// if (qrand() % 10 < 5) { emit failedMatchedIris(); - } - else { - emit findMatchedIris("1"); - } +// } +// else { +// emit findMatchedIris("1"); +// } } } diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri index d0942ce..43a1bd3 100644 --- a/casic/iris/casicIris.pri +++ b/casic/iris/casicIris.pri @@ -1,4 +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/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp index b64d747..6d2553e 100644 --- a/device/iris/CasicIrisRecState.cpp +++ b/device/iris/CasicIrisRecState.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisRecState.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" #include "utils/UtilInclude.h" CasicIrisRecState::CasicIrisRecState() @@ -50,14 +54,14 @@ QJsonObject eyeObj; eyeObj.insert("hasEye", irisInfo->hasEye); - QJsonArray rectArray; - rectArray.append(irisInfo->eyeRect.x); - rectArray.append(irisInfo->eyeRect.y); - rectArray.append(irisInfo->eyeRect.width); - rectArray.append(irisInfo->eyeRect.height); - eyeObj.insert("faceRect", rectArray); +// QJsonArray rectArray; +// rectArray.append(irisInfo->eyeRect.x); +// rectArray.append(irisInfo->eyeRect.y); +// rectArray.append(irisInfo->eyeRect.width); +// rectArray.append(irisInfo->eyeRect.height); +// eyeObj.insert("faceRect", rectArray); - obj.insert("eye", eyeObj); +// obj.insert("eye", eyeObj); return obj; } diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp index 6869ece..08c8227 100644 --- a/device/iris/IrisRecogProcess.cpp +++ b/device/iris/IrisRecogProcess.cpp @@ -11,11 +11,11 @@ this->exit = false; // 连接算法服务 -// clientUtil = new SocketClientUtil(this); -// clientUtil->connect("127.0.0.1", 2015); + clientUtil = new SocketClientUtil(this); + clientUtil->connect("192.168.83.223", 50007); // 调用socket发送消息 -// connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); } IrisRecogProcess::~IrisRecogProcess() @@ -64,9 +64,6 @@ continue; } -// LOG(TRACE) << QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString(); -// LOG_TRACE(QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString()); - // 取出虹膜图像栈中的一条数据 CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); @@ -75,7 +72,7 @@ qDebug() << QString("[IrisRecogProcess] 取出虹膜图像"); // 调用找眼分类器算法 - casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(200); irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); // 没有找到眼睛则返回 @@ -104,34 +101,107 @@ CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; - CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; - CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; - CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; - CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); +// CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; +// CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; +// CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; +// CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + +// irisInfo.matData = cv::imread("d:\\irisLogs\\photo\\left.bmp", 0); QElapsedTimer timer; timer.start(); - QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); - data.prepend("[-m]"); // 识别 + // 进行预处理 如图像 + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisPreProcess(irisInfo); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 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(); + + 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 < 320 * 240) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= 320 * 240 && i < 2 * 320 * 240) + recvMask[i - 320 * 240] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * 320 * 240] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(240, 320, CV_8UC1, recvPupil); + cv::Mat mask(240, 320, CV_8UC1, recvMask); + cv::Mat iris(240, 320, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; +/* + std::string pupilSeg = QString("%1\\%2\\%3-pupil-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1\\%2\\%3-mask-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1\\%2\\%3-iris-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + qDebug() << QString("算法分割图像成功[%1 ms]").arg(timer.elapsed()); + + // 分割后的后处理 + if(cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0|| cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(200); // 200ms后再判断 + continue; + } + // 状态修改为特征提取 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; - QTimer toTimer; - QEventLoop loop; - toTimer.singleShot(3000, &loop, &QEventLoop::quit); -// connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); - loop.exec(); + timer.restart(); + qDebug() << "开始提取特征"; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1\\%2\\%3-code.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskFilename = QString("%1\\%2\\%3-mask.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + qDebug() << QString("虹膜图像编码成功[%1 ms]").arg(timer.elapsed()); -// QByteArray matchResponse = clientUtil->getResponse(); - QByteArray matchResponse; + // 开始匹配 + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + float score = casic::iris::CasicIrisInterface::getInstance().calculateMatchScore(feature, irisInfo); + qDebug() << score; + if (score <= 0.32) { + // 找到匹配结果 + emit findMatchedIris(feature.personId); + + break; + } + } + + // 没有匹配上 + addOneTryCount(); + +// QByteArray matchResponse; // LOG(INFO) << QString("算法匹配结果").toStdString() << matchResponse.toStdString(); // LOG_INFO("算法匹配结果 {}",matchResponse.toStdString()); - +/* // 算法调用返回成功 if (matchResponse.size() > 0) { @@ -172,7 +242,7 @@ { // 超时返回则尝试次数+1 addOneTryCount(); - } + }*/ } } @@ -213,11 +283,11 @@ // ProMemory::getInstance().irisCam->stopCapture(); ProMemory::getInstance().clearIrisQueue(); - if (qrand() % 10 < 5) { +// if (qrand() % 10 < 5) { emit failedMatchedIris(); - } - else { - emit findMatchedIris("1"); - } +// } +// else { +// emit findMatchedIris("1"); +// } } } diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h index c4fc648..76ef115 100644 --- a/device/iris/IrisRecogProcess.h +++ b/device/iris/IrisRecogProcess.h @@ -32,7 +32,7 @@ volatile bool exit; private: -// SocketClientUtil * clientUtil; + SocketClientUtil * clientUtil; void afterRecogAction(); diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri index d0942ce..43a1bd3 100644 --- a/casic/iris/casicIris.pri +++ b/casic/iris/casicIris.pri @@ -1,4 +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/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp index b64d747..6d2553e 100644 --- a/device/iris/CasicIrisRecState.cpp +++ b/device/iris/CasicIrisRecState.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisRecState.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" #include "utils/UtilInclude.h" CasicIrisRecState::CasicIrisRecState() @@ -50,14 +54,14 @@ QJsonObject eyeObj; eyeObj.insert("hasEye", irisInfo->hasEye); - QJsonArray rectArray; - rectArray.append(irisInfo->eyeRect.x); - rectArray.append(irisInfo->eyeRect.y); - rectArray.append(irisInfo->eyeRect.width); - rectArray.append(irisInfo->eyeRect.height); - eyeObj.insert("faceRect", rectArray); +// QJsonArray rectArray; +// rectArray.append(irisInfo->eyeRect.x); +// rectArray.append(irisInfo->eyeRect.y); +// rectArray.append(irisInfo->eyeRect.width); +// rectArray.append(irisInfo->eyeRect.height); +// eyeObj.insert("faceRect", rectArray); - obj.insert("eye", eyeObj); +// obj.insert("eye", eyeObj); return obj; } diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp index 6869ece..08c8227 100644 --- a/device/iris/IrisRecogProcess.cpp +++ b/device/iris/IrisRecogProcess.cpp @@ -11,11 +11,11 @@ this->exit = false; // 连接算法服务 -// clientUtil = new SocketClientUtil(this); -// clientUtil->connect("127.0.0.1", 2015); + clientUtil = new SocketClientUtil(this); + clientUtil->connect("192.168.83.223", 50007); // 调用socket发送消息 -// connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); } IrisRecogProcess::~IrisRecogProcess() @@ -64,9 +64,6 @@ continue; } -// LOG(TRACE) << QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString(); -// LOG_TRACE(QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString()); - // 取出虹膜图像栈中的一条数据 CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); @@ -75,7 +72,7 @@ qDebug() << QString("[IrisRecogProcess] 取出虹膜图像"); // 调用找眼分类器算法 - casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(200); irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); // 没有找到眼睛则返回 @@ -104,34 +101,107 @@ CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; - CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; - CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; - CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; - CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); +// CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; +// CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; +// CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; +// CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + +// irisInfo.matData = cv::imread("d:\\irisLogs\\photo\\left.bmp", 0); QElapsedTimer timer; timer.start(); - QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); - data.prepend("[-m]"); // 识别 + // 进行预处理 如图像 + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisPreProcess(irisInfo); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 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(); + + 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 < 320 * 240) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= 320 * 240 && i < 2 * 320 * 240) + recvMask[i - 320 * 240] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * 320 * 240] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(240, 320, CV_8UC1, recvPupil); + cv::Mat mask(240, 320, CV_8UC1, recvMask); + cv::Mat iris(240, 320, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; +/* + std::string pupilSeg = QString("%1\\%2\\%3-pupil-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1\\%2\\%3-mask-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1\\%2\\%3-iris-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + qDebug() << QString("算法分割图像成功[%1 ms]").arg(timer.elapsed()); + + // 分割后的后处理 + if(cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0|| cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(200); // 200ms后再判断 + continue; + } + // 状态修改为特征提取 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; - QTimer toTimer; - QEventLoop loop; - toTimer.singleShot(3000, &loop, &QEventLoop::quit); -// connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); - loop.exec(); + timer.restart(); + qDebug() << "开始提取特征"; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1\\%2\\%3-code.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskFilename = QString("%1\\%2\\%3-mask.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + qDebug() << QString("虹膜图像编码成功[%1 ms]").arg(timer.elapsed()); -// QByteArray matchResponse = clientUtil->getResponse(); - QByteArray matchResponse; + // 开始匹配 + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + float score = casic::iris::CasicIrisInterface::getInstance().calculateMatchScore(feature, irisInfo); + qDebug() << score; + if (score <= 0.32) { + // 找到匹配结果 + emit findMatchedIris(feature.personId); + + break; + } + } + + // 没有匹配上 + addOneTryCount(); + +// QByteArray matchResponse; // LOG(INFO) << QString("算法匹配结果").toStdString() << matchResponse.toStdString(); // LOG_INFO("算法匹配结果 {}",matchResponse.toStdString()); - +/* // 算法调用返回成功 if (matchResponse.size() > 0) { @@ -172,7 +242,7 @@ { // 超时返回则尝试次数+1 addOneTryCount(); - } + }*/ } } @@ -213,11 +283,11 @@ // ProMemory::getInstance().irisCam->stopCapture(); ProMemory::getInstance().clearIrisQueue(); - if (qrand() % 10 < 5) { +// if (qrand() % 10 < 5) { emit failedMatchedIris(); - } - else { - emit findMatchedIris("1"); - } +// } +// else { +// emit findMatchedIris("1"); +// } } } diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h index c4fc648..76ef115 100644 --- a/device/iris/IrisRecogProcess.h +++ b/device/iris/IrisRecogProcess.h @@ -32,7 +32,7 @@ volatile bool exit; private: -// SocketClientUtil * clientUtil; + SocketClientUtil * clientUtil; void afterRecogAction(); 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/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri index d0942ce..43a1bd3 100644 --- a/casic/iris/casicIris.pri +++ b/casic/iris/casicIris.pri @@ -1,4 +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/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp index b64d747..6d2553e 100644 --- a/device/iris/CasicIrisRecState.cpp +++ b/device/iris/CasicIrisRecState.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisRecState.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" #include "utils/UtilInclude.h" CasicIrisRecState::CasicIrisRecState() @@ -50,14 +54,14 @@ QJsonObject eyeObj; eyeObj.insert("hasEye", irisInfo->hasEye); - QJsonArray rectArray; - rectArray.append(irisInfo->eyeRect.x); - rectArray.append(irisInfo->eyeRect.y); - rectArray.append(irisInfo->eyeRect.width); - rectArray.append(irisInfo->eyeRect.height); - eyeObj.insert("faceRect", rectArray); +// QJsonArray rectArray; +// rectArray.append(irisInfo->eyeRect.x); +// rectArray.append(irisInfo->eyeRect.y); +// rectArray.append(irisInfo->eyeRect.width); +// rectArray.append(irisInfo->eyeRect.height); +// eyeObj.insert("faceRect", rectArray); - obj.insert("eye", eyeObj); +// obj.insert("eye", eyeObj); return obj; } diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp index 6869ece..08c8227 100644 --- a/device/iris/IrisRecogProcess.cpp +++ b/device/iris/IrisRecogProcess.cpp @@ -11,11 +11,11 @@ this->exit = false; // 连接算法服务 -// clientUtil = new SocketClientUtil(this); -// clientUtil->connect("127.0.0.1", 2015); + clientUtil = new SocketClientUtil(this); + clientUtil->connect("192.168.83.223", 50007); // 调用socket发送消息 -// connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); } IrisRecogProcess::~IrisRecogProcess() @@ -64,9 +64,6 @@ continue; } -// LOG(TRACE) << QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString(); -// LOG_TRACE(QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString()); - // 取出虹膜图像栈中的一条数据 CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); @@ -75,7 +72,7 @@ qDebug() << QString("[IrisRecogProcess] 取出虹膜图像"); // 调用找眼分类器算法 - casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(200); irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); // 没有找到眼睛则返回 @@ -104,34 +101,107 @@ CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; - CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; - CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; - CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; - CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); +// CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; +// CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; +// CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; +// CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + +// irisInfo.matData = cv::imread("d:\\irisLogs\\photo\\left.bmp", 0); QElapsedTimer timer; timer.start(); - QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); - data.prepend("[-m]"); // 识别 + // 进行预处理 如图像 + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisPreProcess(irisInfo); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 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(); + + 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 < 320 * 240) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= 320 * 240 && i < 2 * 320 * 240) + recvMask[i - 320 * 240] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * 320 * 240] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(240, 320, CV_8UC1, recvPupil); + cv::Mat mask(240, 320, CV_8UC1, recvMask); + cv::Mat iris(240, 320, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; +/* + std::string pupilSeg = QString("%1\\%2\\%3-pupil-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1\\%2\\%3-mask-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1\\%2\\%3-iris-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + qDebug() << QString("算法分割图像成功[%1 ms]").arg(timer.elapsed()); + + // 分割后的后处理 + if(cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0|| cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(200); // 200ms后再判断 + continue; + } + // 状态修改为特征提取 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; - QTimer toTimer; - QEventLoop loop; - toTimer.singleShot(3000, &loop, &QEventLoop::quit); -// connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); - loop.exec(); + timer.restart(); + qDebug() << "开始提取特征"; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1\\%2\\%3-code.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskFilename = QString("%1\\%2\\%3-mask.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + qDebug() << QString("虹膜图像编码成功[%1 ms]").arg(timer.elapsed()); -// QByteArray matchResponse = clientUtil->getResponse(); - QByteArray matchResponse; + // 开始匹配 + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + float score = casic::iris::CasicIrisInterface::getInstance().calculateMatchScore(feature, irisInfo); + qDebug() << score; + if (score <= 0.32) { + // 找到匹配结果 + emit findMatchedIris(feature.personId); + + break; + } + } + + // 没有匹配上 + addOneTryCount(); + +// QByteArray matchResponse; // LOG(INFO) << QString("算法匹配结果").toStdString() << matchResponse.toStdString(); // LOG_INFO("算法匹配结果 {}",matchResponse.toStdString()); - +/* // 算法调用返回成功 if (matchResponse.size() > 0) { @@ -172,7 +242,7 @@ { // 超时返回则尝试次数+1 addOneTryCount(); - } + }*/ } } @@ -213,11 +283,11 @@ // ProMemory::getInstance().irisCam->stopCapture(); ProMemory::getInstance().clearIrisQueue(); - if (qrand() % 10 < 5) { +// if (qrand() % 10 < 5) { emit failedMatchedIris(); - } - else { - emit findMatchedIris("1"); - } +// } +// else { +// emit findMatchedIris("1"); +// } } } diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h index c4fc648..76ef115 100644 --- a/device/iris/IrisRecogProcess.h +++ b/device/iris/IrisRecogProcess.h @@ -32,7 +32,7 @@ volatile bool exit; private: -// SocketClientUtil * clientUtil; + SocketClientUtil * clientUtil; void afterRecogAction(); 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/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri index d0942ce..43a1bd3 100644 --- a/casic/iris/casicIris.pri +++ b/casic/iris/casicIris.pri @@ -1,4 +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/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp index b64d747..6d2553e 100644 --- a/device/iris/CasicIrisRecState.cpp +++ b/device/iris/CasicIrisRecState.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisRecState.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" #include "utils/UtilInclude.h" CasicIrisRecState::CasicIrisRecState() @@ -50,14 +54,14 @@ QJsonObject eyeObj; eyeObj.insert("hasEye", irisInfo->hasEye); - QJsonArray rectArray; - rectArray.append(irisInfo->eyeRect.x); - rectArray.append(irisInfo->eyeRect.y); - rectArray.append(irisInfo->eyeRect.width); - rectArray.append(irisInfo->eyeRect.height); - eyeObj.insert("faceRect", rectArray); +// QJsonArray rectArray; +// rectArray.append(irisInfo->eyeRect.x); +// rectArray.append(irisInfo->eyeRect.y); +// rectArray.append(irisInfo->eyeRect.width); +// rectArray.append(irisInfo->eyeRect.height); +// eyeObj.insert("faceRect", rectArray); - obj.insert("eye", eyeObj); +// obj.insert("eye", eyeObj); return obj; } diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp index 6869ece..08c8227 100644 --- a/device/iris/IrisRecogProcess.cpp +++ b/device/iris/IrisRecogProcess.cpp @@ -11,11 +11,11 @@ this->exit = false; // 连接算法服务 -// clientUtil = new SocketClientUtil(this); -// clientUtil->connect("127.0.0.1", 2015); + clientUtil = new SocketClientUtil(this); + clientUtil->connect("192.168.83.223", 50007); // 调用socket发送消息 -// connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); } IrisRecogProcess::~IrisRecogProcess() @@ -64,9 +64,6 @@ continue; } -// LOG(TRACE) << QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString(); -// LOG_TRACE(QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString()); - // 取出虹膜图像栈中的一条数据 CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); @@ -75,7 +72,7 @@ qDebug() << QString("[IrisRecogProcess] 取出虹膜图像"); // 调用找眼分类器算法 - casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(200); irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); // 没有找到眼睛则返回 @@ -104,34 +101,107 @@ CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; - CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; - CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; - CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; - CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); +// CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; +// CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; +// CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; +// CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + +// irisInfo.matData = cv::imread("d:\\irisLogs\\photo\\left.bmp", 0); QElapsedTimer timer; timer.start(); - QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); - data.prepend("[-m]"); // 识别 + // 进行预处理 如图像 + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisPreProcess(irisInfo); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 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(); + + 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 < 320 * 240) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= 320 * 240 && i < 2 * 320 * 240) + recvMask[i - 320 * 240] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * 320 * 240] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(240, 320, CV_8UC1, recvPupil); + cv::Mat mask(240, 320, CV_8UC1, recvMask); + cv::Mat iris(240, 320, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; +/* + std::string pupilSeg = QString("%1\\%2\\%3-pupil-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1\\%2\\%3-mask-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1\\%2\\%3-iris-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + qDebug() << QString("算法分割图像成功[%1 ms]").arg(timer.elapsed()); + + // 分割后的后处理 + if(cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0|| cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(200); // 200ms后再判断 + continue; + } + // 状态修改为特征提取 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; - QTimer toTimer; - QEventLoop loop; - toTimer.singleShot(3000, &loop, &QEventLoop::quit); -// connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); - loop.exec(); + timer.restart(); + qDebug() << "开始提取特征"; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1\\%2\\%3-code.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskFilename = QString("%1\\%2\\%3-mask.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + qDebug() << QString("虹膜图像编码成功[%1 ms]").arg(timer.elapsed()); -// QByteArray matchResponse = clientUtil->getResponse(); - QByteArray matchResponse; + // 开始匹配 + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + float score = casic::iris::CasicIrisInterface::getInstance().calculateMatchScore(feature, irisInfo); + qDebug() << score; + if (score <= 0.32) { + // 找到匹配结果 + emit findMatchedIris(feature.personId); + + break; + } + } + + // 没有匹配上 + addOneTryCount(); + +// QByteArray matchResponse; // LOG(INFO) << QString("算法匹配结果").toStdString() << matchResponse.toStdString(); // LOG_INFO("算法匹配结果 {}",matchResponse.toStdString()); - +/* // 算法调用返回成功 if (matchResponse.size() > 0) { @@ -172,7 +242,7 @@ { // 超时返回则尝试次数+1 addOneTryCount(); - } + }*/ } } @@ -213,11 +283,11 @@ // ProMemory::getInstance().irisCam->stopCapture(); ProMemory::getInstance().clearIrisQueue(); - if (qrand() % 10 < 5) { +// if (qrand() % 10 < 5) { emit failedMatchedIris(); - } - else { - emit findMatchedIris("1"); - } +// } +// else { +// emit findMatchedIris("1"); +// } } } diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h index c4fc648..76ef115 100644 --- a/device/iris/IrisRecogProcess.h +++ b/device/iris/IrisRecogProcess.h @@ -32,7 +32,7 @@ volatile bool exit; private: -// SocketClientUtil * clientUtil; + SocketClientUtil * clientUtil; void afterRecogAction(); 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/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..d4e9c5c --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,41 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri index d0942ce..43a1bd3 100644 --- a/casic/iris/casicIris.pri +++ b/casic/iris/casicIris.pri @@ -1,4 +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/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp index b64d747..6d2553e 100644 --- a/device/iris/CasicIrisRecState.cpp +++ b/device/iris/CasicIrisRecState.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisRecState.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" #include "utils/UtilInclude.h" CasicIrisRecState::CasicIrisRecState() @@ -50,14 +54,14 @@ QJsonObject eyeObj; eyeObj.insert("hasEye", irisInfo->hasEye); - QJsonArray rectArray; - rectArray.append(irisInfo->eyeRect.x); - rectArray.append(irisInfo->eyeRect.y); - rectArray.append(irisInfo->eyeRect.width); - rectArray.append(irisInfo->eyeRect.height); - eyeObj.insert("faceRect", rectArray); +// QJsonArray rectArray; +// rectArray.append(irisInfo->eyeRect.x); +// rectArray.append(irisInfo->eyeRect.y); +// rectArray.append(irisInfo->eyeRect.width); +// rectArray.append(irisInfo->eyeRect.height); +// eyeObj.insert("faceRect", rectArray); - obj.insert("eye", eyeObj); +// obj.insert("eye", eyeObj); return obj; } diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp index 6869ece..08c8227 100644 --- a/device/iris/IrisRecogProcess.cpp +++ b/device/iris/IrisRecogProcess.cpp @@ -11,11 +11,11 @@ this->exit = false; // 连接算法服务 -// clientUtil = new SocketClientUtil(this); -// clientUtil->connect("127.0.0.1", 2015); + clientUtil = new SocketClientUtil(this); + clientUtil->connect("192.168.83.223", 50007); // 调用socket发送消息 -// connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); } IrisRecogProcess::~IrisRecogProcess() @@ -64,9 +64,6 @@ continue; } -// LOG(TRACE) << QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString(); -// LOG_TRACE(QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString()); - // 取出虹膜图像栈中的一条数据 CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); @@ -75,7 +72,7 @@ qDebug() << QString("[IrisRecogProcess] 取出虹膜图像"); // 调用找眼分类器算法 - casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(200); irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); // 没有找到眼睛则返回 @@ -104,34 +101,107 @@ CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; - CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; - CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; - CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; - CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); +// CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; +// CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; +// CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; +// CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + +// irisInfo.matData = cv::imread("d:\\irisLogs\\photo\\left.bmp", 0); QElapsedTimer timer; timer.start(); - QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); - data.prepend("[-m]"); // 识别 + // 进行预处理 如图像 + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisPreProcess(irisInfo); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 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(); + + 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 < 320 * 240) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= 320 * 240 && i < 2 * 320 * 240) + recvMask[i - 320 * 240] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * 320 * 240] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(240, 320, CV_8UC1, recvPupil); + cv::Mat mask(240, 320, CV_8UC1, recvMask); + cv::Mat iris(240, 320, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; +/* + std::string pupilSeg = QString("%1\\%2\\%3-pupil-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1\\%2\\%3-mask-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1\\%2\\%3-iris-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + qDebug() << QString("算法分割图像成功[%1 ms]").arg(timer.elapsed()); + + // 分割后的后处理 + if(cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0|| cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(200); // 200ms后再判断 + continue; + } + // 状态修改为特征提取 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; - QTimer toTimer; - QEventLoop loop; - toTimer.singleShot(3000, &loop, &QEventLoop::quit); -// connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); - loop.exec(); + timer.restart(); + qDebug() << "开始提取特征"; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1\\%2\\%3-code.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskFilename = QString("%1\\%2\\%3-mask.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + qDebug() << QString("虹膜图像编码成功[%1 ms]").arg(timer.elapsed()); -// QByteArray matchResponse = clientUtil->getResponse(); - QByteArray matchResponse; + // 开始匹配 + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + float score = casic::iris::CasicIrisInterface::getInstance().calculateMatchScore(feature, irisInfo); + qDebug() << score; + if (score <= 0.32) { + // 找到匹配结果 + emit findMatchedIris(feature.personId); + + break; + } + } + + // 没有匹配上 + addOneTryCount(); + +// QByteArray matchResponse; // LOG(INFO) << QString("算法匹配结果").toStdString() << matchResponse.toStdString(); // LOG_INFO("算法匹配结果 {}",matchResponse.toStdString()); - +/* // 算法调用返回成功 if (matchResponse.size() > 0) { @@ -172,7 +242,7 @@ { // 超时返回则尝试次数+1 addOneTryCount(); - } + }*/ } } @@ -213,11 +283,11 @@ // ProMemory::getInstance().irisCam->stopCapture(); ProMemory::getInstance().clearIrisQueue(); - if (qrand() % 10 < 5) { +// if (qrand() % 10 < 5) { emit failedMatchedIris(); - } - else { - emit findMatchedIris("1"); - } +// } +// else { +// emit findMatchedIris("1"); +// } } } diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h index c4fc648..76ef115 100644 --- a/device/iris/IrisRecogProcess.h +++ b/device/iris/IrisRecogProcess.h @@ -32,7 +32,7 @@ volatile bool exit; private: -// SocketClientUtil * clientUtil; + SocketClientUtil * clientUtil; void afterRecogAction(); 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/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..d4e9c5c --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,41 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..2c833e1 --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,35 @@ +#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(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri index d0942ce..43a1bd3 100644 --- a/casic/iris/casicIris.pri +++ b/casic/iris/casicIris.pri @@ -1,4 +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/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp index b64d747..6d2553e 100644 --- a/device/iris/CasicIrisRecState.cpp +++ b/device/iris/CasicIrisRecState.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisRecState.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" #include "utils/UtilInclude.h" CasicIrisRecState::CasicIrisRecState() @@ -50,14 +54,14 @@ QJsonObject eyeObj; eyeObj.insert("hasEye", irisInfo->hasEye); - QJsonArray rectArray; - rectArray.append(irisInfo->eyeRect.x); - rectArray.append(irisInfo->eyeRect.y); - rectArray.append(irisInfo->eyeRect.width); - rectArray.append(irisInfo->eyeRect.height); - eyeObj.insert("faceRect", rectArray); +// QJsonArray rectArray; +// rectArray.append(irisInfo->eyeRect.x); +// rectArray.append(irisInfo->eyeRect.y); +// rectArray.append(irisInfo->eyeRect.width); +// rectArray.append(irisInfo->eyeRect.height); +// eyeObj.insert("faceRect", rectArray); - obj.insert("eye", eyeObj); +// obj.insert("eye", eyeObj); return obj; } diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp index 6869ece..08c8227 100644 --- a/device/iris/IrisRecogProcess.cpp +++ b/device/iris/IrisRecogProcess.cpp @@ -11,11 +11,11 @@ this->exit = false; // 连接算法服务 -// clientUtil = new SocketClientUtil(this); -// clientUtil->connect("127.0.0.1", 2015); + clientUtil = new SocketClientUtil(this); + clientUtil->connect("192.168.83.223", 50007); // 调用socket发送消息 -// connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); } IrisRecogProcess::~IrisRecogProcess() @@ -64,9 +64,6 @@ continue; } -// LOG(TRACE) << QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString(); -// LOG_TRACE(QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString()); - // 取出虹膜图像栈中的一条数据 CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); @@ -75,7 +72,7 @@ qDebug() << QString("[IrisRecogProcess] 取出虹膜图像"); // 调用找眼分类器算法 - casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(200); irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); // 没有找到眼睛则返回 @@ -104,34 +101,107 @@ CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; - CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; - CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; - CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; - CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); +// CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; +// CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; +// CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; +// CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + +// irisInfo.matData = cv::imread("d:\\irisLogs\\photo\\left.bmp", 0); QElapsedTimer timer; timer.start(); - QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); - data.prepend("[-m]"); // 识别 + // 进行预处理 如图像 + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisPreProcess(irisInfo); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 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(); + + 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 < 320 * 240) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= 320 * 240 && i < 2 * 320 * 240) + recvMask[i - 320 * 240] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * 320 * 240] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(240, 320, CV_8UC1, recvPupil); + cv::Mat mask(240, 320, CV_8UC1, recvMask); + cv::Mat iris(240, 320, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; +/* + std::string pupilSeg = QString("%1\\%2\\%3-pupil-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1\\%2\\%3-mask-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1\\%2\\%3-iris-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + qDebug() << QString("算法分割图像成功[%1 ms]").arg(timer.elapsed()); + + // 分割后的后处理 + if(cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0|| cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(200); // 200ms后再判断 + continue; + } + // 状态修改为特征提取 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; - QTimer toTimer; - QEventLoop loop; - toTimer.singleShot(3000, &loop, &QEventLoop::quit); -// connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); - loop.exec(); + timer.restart(); + qDebug() << "开始提取特征"; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1\\%2\\%3-code.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskFilename = QString("%1\\%2\\%3-mask.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + qDebug() << QString("虹膜图像编码成功[%1 ms]").arg(timer.elapsed()); -// QByteArray matchResponse = clientUtil->getResponse(); - QByteArray matchResponse; + // 开始匹配 + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + float score = casic::iris::CasicIrisInterface::getInstance().calculateMatchScore(feature, irisInfo); + qDebug() << score; + if (score <= 0.32) { + // 找到匹配结果 + emit findMatchedIris(feature.personId); + + break; + } + } + + // 没有匹配上 + addOneTryCount(); + +// QByteArray matchResponse; // LOG(INFO) << QString("算法匹配结果").toStdString() << matchResponse.toStdString(); // LOG_INFO("算法匹配结果 {}",matchResponse.toStdString()); - +/* // 算法调用返回成功 if (matchResponse.size() > 0) { @@ -172,7 +242,7 @@ { // 超时返回则尝试次数+1 addOneTryCount(); - } + }*/ } } @@ -213,11 +283,11 @@ // ProMemory::getInstance().irisCam->stopCapture(); ProMemory::getInstance().clearIrisQueue(); - if (qrand() % 10 < 5) { +// if (qrand() % 10 < 5) { emit failedMatchedIris(); - } - else { - emit findMatchedIris("1"); - } +// } +// else { +// emit findMatchedIris("1"); +// } } } diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h index c4fc648..76ef115 100644 --- a/device/iris/IrisRecogProcess.h +++ b/device/iris/IrisRecogProcess.h @@ -32,7 +32,7 @@ volatile bool exit; private: -// SocketClientUtil * clientUtil; + SocketClientUtil * clientUtil; void afterRecogAction(); 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/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..d4e9c5c --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,41 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..2c833e1 --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,35 @@ +#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(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/UtilInclude.h b/utils/UtilInclude.h index d0c934c..b5e2ce9 100644 --- a/utils/UtilInclude.h +++ b/utils/UtilInclude.h @@ -2,10 +2,11 @@ #define UTILINCLUDE_H #include -//#include "ByteUtil.h" +#include "ByteUtil.h" #include "ImageUtil.h" //#include "LogUtil.h" #include "SettingConfig.h" +#include "SocketClientUtil.h" #include "SpeakerUtil.h" #include "TimeCounterUtil.h" //#include "UDPClientUtil.h" diff --git a/CasicIrisIdentify.pro b/CasicIrisIdentify.pro index fc8a757..5e411d7 100644 --- a/CasicIrisIdentify.pro +++ b/CasicIrisIdentify.pro @@ -61,4 +61,4 @@ INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include - +INCLUDEPATH += $$PWD/Eigen diff --git a/MainWindowForm.cpp b/MainWindowForm.cpp index 8abbd6d..79a320b 100644 --- a/MainWindowForm.cpp +++ b/MainWindowForm.cpp @@ -55,6 +55,8 @@ ProMemory::getInstance().widgeFrame = AppConstants::WidgeFrameName::IDENTIFY_FORM; ui->wdgtStatced->setCurrentWidget(identifyForm); + ProMemory::getInstance().initIrisFeatures(); + // 开始虹膜相机拍图 ProMemory::getInstance().irisCam->startCapture(); diff --git a/ProMemory.cpp b/ProMemory.cpp index 2fdd9e2..43cb6a0 100644 --- a/ProMemory.cpp +++ b/ProMemory.cpp @@ -76,8 +76,31 @@ mutex.unlock(); } +void ProMemory::initIrisFeatures() +{ + CasicIrisFeature leftFeature; + leftFeature.irisId = 1; + leftFeature.personId = "11"; + leftFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\184300-code.bmp", 0); + leftFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\184300-mask.bmp", 0); +/* + CasicIrisFeature rightFeature; + rightFeature.irisId = 2; + rightFeature.personId = "11"; + rightFeature.irisCode = cv::imread("d:\\irisLogs\\photo\\165136-code.bmp", 0); + rightFeature.maskNorm = cv::imread("d:\\irisLogs\\photo\\165136-mask.bmp", 0); +*/ + this->irisFeatures.append(leftFeature); +// this->irisFeatures.append(rightFeature); +} +/* void ProMemory::initIrisFeatures(QString personId) { // irisRegistPro->sendDataToExract(QByteArray(QString("[-u]").append(personId).toLocal8Bit())); } +*/ +QVector ProMemory::getIrisFeatures() +{ + return this->irisFeatures; +} diff --git a/ProMemory.h b/ProMemory.h index 7447e35..f57594a 100644 --- a/ProMemory.h +++ b/ProMemory.h @@ -7,6 +7,7 @@ #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" @@ -36,16 +37,21 @@ volatile int widgeFrame = 0; // 当前显示的界面 volatile int appState = 0; // 当前程序所处的状态 - void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + void initIrisFeatures(); +// void initIrisFeatures(QString personId = ""); // 初始化虹膜特征值集合 + + QVector getIrisFeatures(); IrisCameraController * irisCam; IrisRecogProcess * irisRecogPro; + SocketClientUtil client; + private: ProMemory(); QStack irisQueue; // 虹膜信息队列 - QVector irisFeatures; // 虹膜特征值集合 + QVector irisFeatures; // 虹膜特征值集合 }; #endif // PROMEMORY_H diff --git a/casic/iris/CasicIrisInfo.h b/casic/iris/CasicIrisInfo.h index 7699370..6b8b1ee 100644 --- a/casic/iris/CasicIrisInfo.h +++ b/casic/iris/CasicIrisInfo.h @@ -3,20 +3,34 @@ #include #include "opencv2/opencv.hpp" +#include "CasicSegResult.h" struct CasicIrisInfo { // 是否有眼睛, 默认为false bool hasEye = false; - cv::Rect eyeRect; // 眼睛的位置(x, y, width, height) // 眼部图像 // 后续计算需要使用 - QImage data; +// QImage data; cv::Mat matData; + cv::Mat maskNorm; + cv::Mat irisCode; + + iristrt::CasicSegResult segResult; + // 字节数组形式的特征码 - QByteArray irisCode; + 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 index 5c46e5b..7f1d635 100644 --- a/casic/iris/CasicIrisInterface.cpp +++ b/casic/iris/CasicIrisInterface.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisInterface.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisInterface.h" #include #include @@ -6,7 +10,10 @@ casic::iris::CasicIrisInterface::CasicIrisInterface() { + rec = iristrt::CasicIrisRec(gaborFilterFileName, applicationPointsFileName); + cascade = new cv::CascadeClassifier(); + cascade->load(cascadeName); } casic::iris::CasicIrisInterface::~CasicIrisInterface() @@ -65,9 +72,9 @@ .arg(rect.at(0).width).arg(rect.at(0).height); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); - irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); +// irisInfo.data = ImageUtil::MatImageToQImage(irisInfo.matData); return irisInfo; } @@ -146,10 +153,10 @@ // cv::imwrite(QString("d:\\irisLogs\\%1.bmp").arg(QDateTime::currentSecsSinceEpoch()).toStdString(), halfSize); irisInfo.hasEye = true; - irisInfo.eyeRect = rect.at(0); +// irisInfo.eyeRect = rect.at(0); irisInfo.matData = halfSize; - irisInfo.data = ImageUtil::MatImageToQImage(halfSize); +// irisInfo.data = ImageUtil::MatImageToQImage(halfSize); return irisInfo; } @@ -185,3 +192,62 @@ return irisInfo; } + +CasicIrisInfo casic::iris::CasicIrisInterface::irisPreProcess(CasicIrisInfo irisInfo) +{ + + std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(filename, irisInfo.matData); + + cv::Mat preMat = irisInfo.matData; + + // 图像转换为RGB的彩色图像 + cv::cvtColor(preMat, preMat, cv::COLOR_GRAY2RGB); + + // 缩放到320 * 240 + cv::resize(preMat, preMat, cv::Size(320, 240)); + irisInfo.matData = preMat; + +// std::string filename = QString("%1\\%2\\%3.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); +// cv::imwrite(filename, irisInfo.matData); + + return irisInfo; +} + + +CasicIrisInfo casic::iris::CasicIrisInterface::irisEncode(CasicIrisInfo irisInfo) +{ + std::vector irisCircle, pupilCircle; // x,y,r + iristrt::CasicSegPostProcess segPost; + bool flag = segPost.postProcess(irisInfo.segResult.irisMask, irisInfo.segResult.outerCircle, irisInfo.segResult.innerMask, irisCircle, pupilCircle); + if(!flag || irisCircle.size() <= 0 || pupilCircle.size() <= 0) + { + return irisInfo; + } + // std::cout << "finish postProcess " << irisCircle[0] << " " << irisCircle[1] << " " << irisCircle[2] << " " << pupilCircle[0] << " " << pupilCircle[1] << " " << pupilCircle[2] << std::endl; + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + int dis = iristrt::point_distance(iris_x,iris_y,pupil_x,pupil_y); + if(!(dis + pupil_r < iris_r)) + { + // pupil circle doesn't contained in iris circle + return irisInfo; + } + + cv::Mat imageNorm = rec.normalize(irisInfo.matData, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + irisInfo.maskNorm = rec.normalize(irisInfo.segResult.irisMask, irisCircle[0], irisCircle[1], irisCircle[2], pupilCircle[0], pupilCircle[1], pupilCircle[2]); + // std::cout << "finish normalize" << std::endl; + + irisInfo.irisCode = rec.encodeToImage(imageNorm); + + return irisInfo; +} + +float casic::iris::CasicIrisInterface::calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo) +{ + return rec.matchImage(feature.irisCode, irisInfo.irisCode, feature.maskNorm, irisInfo.maskNorm); +} diff --git a/casic/iris/CasicIrisInterface.h b/casic/iris/CasicIrisInterface.h index 87ed97e..50c0566 100644 --- a/casic/iris/CasicIrisInterface.h +++ b/casic/iris/CasicIrisInterface.h @@ -2,6 +2,9 @@ #define CASICIRISINTERFACE_H #include "CasicIrisInfo.h" +#include "CasicSegPostProcess.h" +#include "CasicIrisUtil.h" +#include "CasicIrisRec.h" #include "utils/UtilInclude.h" namespace casic { @@ -22,7 +25,10 @@ CasicIrisInfo findAndCutEye(CasicIrisInfo irisInfo); CasicIrisInfo irisQualityAssess(CasicIrisInfo irisInfo); CasicIrisInfo irisCodeExtract(CasicIrisInfo irisInfo); - CasicIrisInfo irisMatch(CasicIrisInfo irisInfo); + + CasicIrisInfo irisPreProcess(CasicIrisInfo irisInfo); + CasicIrisInfo irisEncode(CasicIrisInfo irisInfo); + float calculateMatchScore(CasicIrisFeature feature, CasicIrisInfo irisInfo); void setCascadeFile(QString filename); void setMinEyeSize(int minEyeSize); @@ -32,11 +38,14 @@ 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 = 1.0; + float cutRatio = 0.8; cv::CascadeClassifier * cascade; + iristrt::CasicIrisRec rec; }; } } diff --git a/casic/iris/CasicIrisRec.cpp b/casic/iris/CasicIrisRec.cpp new file mode 100644 index 0000000..5f6f3d9 --- /dev/null +++ b/casic/iris/CasicIrisRec.cpp @@ -0,0 +1,300 @@ +#include "CasicIrisRec.h" +#include +#include + +#include +#include +#include +#include + +using Eigen::MatrixXd; +using Eigen::MatrixXi; + +namespace iristrt +{ +CasicIrisRec::CasicIrisRec() +{ + +} + +CasicIrisRec::CasicIrisRec(const char * gaborFilterFileName,const char * applicationPointsFileName) +{ + loadApplicationPoints(applicationPointsFileName); + loadGaborFilters(gaborFilterFileName); +} + +CasicIrisRec::~CasicIrisRec() +{ + +} + +cv::Mat CasicIrisRec::normalize(cv::Mat image, int iris_x, int iris_y, int iris_r,int pupil_x, int pupil_y, int pupil_r) +{ + int width = NORM_WIDTH; + int height = NORM_HEIGHT; + int realHeight = height + 2; + int angledivisions = width - 1; + int rows = image.rows; + int cols = image.cols; + + double ox = pupil_x - iris_x; + double oy = pupil_y - iris_y; + + int sgn = ox <= 0 ? -1 : 1; + if (ox == 0 && oy > 0) { + sgn = 1; + } + + double phi = ox == 0 ? M_PI / 2 : atan(oy / ox); + + MatrixXd a = MatrixXd::Ones(1, width) * (ox * ox + oy * oy); + + MatrixXd thetas(1, width); + for (int i = 0; i < width; i++) { + thetas(0, i) = (2 * M_PI / angledivisions) * i; + } + + MatrixXd b = M_PI - phi - thetas.array(); + b = b.array().cos(); + b = sgn * b; + + MatrixXd item1 = a.array().sqrt() * b.array(); + MatrixXd item2 = a.array() * b.array().pow(2); + MatrixXd item3 = a.array() - (iris_r * iris_r); + MatrixXd r = item1 + (item2 - item3).array().sqrt().matrix(); + r = r.array() - pupil_r; + + MatrixXd rMatrix = MatrixXd::Ones(1, realHeight).transpose() * r; + MatrixXd tmp(1, realHeight); + for (int i = 0; i < realHeight; i++) { + tmp(0, i) = (double)(1.0 / realHeight) * i; + } + rMatrix = rMatrix.array() * (MatrixXd::Ones(angledivisions + 1, 1) * tmp).array().transpose(); + rMatrix = rMatrix.array() + pupil_r; + + MatrixXd new_rMatrix = rMatrix.middleRows(1,realHeight-2); + + MatrixXd xcosMat = MatrixXd::Ones(height, 1) * thetas.array().cos().matrix(); + MatrixXd xsinMat = MatrixXd::Ones(height, 1) * thetas.array().sin().matrix(); + + MatrixXd xo = pupil_x + new_rMatrix.array() * xcosMat.array(); + MatrixXd yo = pupil_y - new_rMatrix.array() * xsinMat.array(); + + MatrixXi xo_i = xo.cast(); + MatrixXi yo_i = yo.cast(); + + if(image.channels() == 3){ + cv::cvtColor(image,image,cv::COLOR_RGB2GRAY); + } + + MatrixXi eigenImage; + + cv::cv2eigen(image, eigenImage); + + MatrixXi normMat = MatrixXi::Zero(xo_i.rows(), xo_i.cols()); + for (int i = 0; i < xo_i.rows(); i++) { + for (int j = 0; j < xo_i.cols(); j++) { + normMat(i, j) = eigenImage(yo_i(i, j), xo_i(i,j)); + } + } + + cv::Mat normImage(image.size(),image.type()); + cv::eigen2cv(normMat, normImage); + normImage.convertTo(normImage, CV_8UC1); + + return normImage; +} + +std::vector CasicIrisRec::loadGaborFilters(const char * gaborFilterFileName) +{ + std::ifstream file(gaborFilterFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load Gabor filters in file " << gaborFilterFileName << std::endl; + } + + // Get the number of filters + int n_filters; + file >> n_filters; + gaborFilters.resize(n_filters); + + //cout << "n_filters " << n_filters << endl; + + // Size of filter + int rows, cols; + + // Loop on each filter + for (int f = 0; f < n_filters; f++) + { + // Get the size of the filter + file >> rows; + file >> cols; + //cout << rows << " " << cols << endl; + // Temporary filter. Will be destroyed at the end of loop + gaborFilters[f] = cv::Mat(rows, cols, CV_32FC1); + + // Set the value at coordinates r,c + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + file >> gaborFilters[f].at(r,c); + } + } + + } // Loop on each filter + + // Close the file + file.close(); + + return gaborFilters; +} + +cv::Mat CasicIrisRec::loadApplicationPoints(const char * applicationPointsFileName) +{ + // Open text file containing the filters + std::ifstream file(applicationPointsFileName, std::ios::in); + if (!file) + { + std::cout << "Cannot load the application points in " << applicationPointsFileName << std::endl; + } + + // Get the number of points + int n_points = 0; + file >> n_points; + + // Allocate memory for the matrix containing the points + applicationPoints = cv::Mat(NORM_HEIGHT, NORM_WIDTH, CV_8UC1); + // Initialize all pixels to "off" + applicationPoints.setTo(cv::Scalar(0)); + + // Local variables + int i, j; + + // Loop on each point + for (int p = 0; p < n_points; p++) + { + // Get the coordinates + file >> i; file >> j; + + // 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); + + //cout << resized.size() << 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); + } + return encodeImage; +} + +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; + +} + +cv::Mat CasicIrisRec::addBorders(cv::Mat pSrc, int width) +{ + cv::Mat result(cv::Size(pSrc.cols + 2 * width, pSrc.rows), pSrc.depth(), pSrc.channels()); + + copyMakeBorder(pSrc, result, 0, 0, width, width, cv::BORDER_REPLICATE, 0); + + for (int i = 0; i < pSrc.rows; i++) { + for (int j = 0; j < width; j++) { + result.at(i, j) = pSrc.at(i, pSrc.cols - width + j); + result.at(i, result.cols - width + j) = pSrc.at(i, j); + } + } + return result; +} + +} diff --git a/casic/iris/CasicIrisRec.h b/casic/iris/CasicIrisRec.h new file mode 100644 index 0000000..f6ca8ac --- /dev/null +++ b/casic/iris/CasicIrisRec.h @@ -0,0 +1,45 @@ +#ifndef CASIC_IRIS_REC +#define CASIC_IRIS_REC + +#define NORM_WIDTH 512 +#define NORM_HEIGHT 64 +#define M_PI 3.1415926535897323846 + +#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); + + float matchImage(cv::Mat code1, cv::Mat code2, cv::Mat normalizedMask1, cv::Mat normalizedMask2); + +private: + + std::vector gaborFilters; + + cv::Mat applicationPoints; + + std::vector loadGaborFilters(const char * gaborFilterFileName); + + cv::Mat loadApplicationPoints(const char * applicationPointsFileName); + + cv::Mat addBorders(cv::Mat pSrc, int width); + +}; // 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..8fdacb0 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.cpp @@ -0,0 +1,285 @@ +#include "CasicSegPostProcess.h" + +#include + +namespace iristrt +{ + +std::vector CasicSegPostProcess::getConnections(cv::Mat input, int connectivity) { + cv::Mat output; + int nLabels = connectedComponents(input, output, connectivity); + output.convertTo(output, CV_8UC1, 1); + + std::vector conns; + for (int i = 1; i <= nLabels; i++) { + cv::Mat dst = cv::Mat::zeros(output.size(), output.type()); + for (int row = 0; row < output.rows; row++) { + for (int col = 0; col < output.cols; col++) { + uchar label = output.at(row, col); + if (label == i) { + dst.at(row, col) = 255; + } + } + } + conns.push_back(dst); + } + return conns; +} + +void CasicSegPostProcess::showConnections(std::vector conns) { + for (int i = 0; i < conns.size(); i++) { + cv::imshow("con", conns[i]); + cv::waitKey(0); + } + cv::destroyAllWindows(); +} + +std::vector CasicSegPostProcess::getTripleSet(std::vector maskConns, + std::vector irisConns, + std::vector pupilConns, + int chessboardDistance) { + std::vector triplet_set; + for (auto maskConn : maskConns) { + for (auto irisConn : irisConns) { + int maskIrisDis = getChessboardDistance(maskConn, irisConn, chessboardDistance); + for (auto pupilConn : pupilConns) { + int maskPupilDis = getChessboardDistance(maskConn, pupilConn, chessboardDistance); + if (maskIrisDis <= chessboardDistance && maskPupilDis <= chessboardDistance) { + CasicTriplet triplet; + triplet.maskConn = maskConn; + triplet.irisConn = irisConn; + triplet.pupilConn = pupilConn; + triplet_set.push_back(triplet); + } + } + } + } + return triplet_set; +} + +void CasicSegPostProcess::showTriple(CasicTriplet triple) { + /*cv::Mat tripleImage; + cv::add(std::get<0>(triple), std::get<1>(triple), tripleImage); + cv::add(std::get<2>(triple), tripleImage, tripleImage); + cv::imshow("tripleImage", tripleImage); + cv::waitKey(0);*/ + + cv::Mat mask = triple.maskConn; + cv::Mat iris = triple.irisConn; + cv::Mat pupil = triple.pupilConn; + + cv::imshow("triple-mask", mask); + cv::waitKey(0); + + cv::imshow("triple-iris", iris); + cv::waitKey(0); + + cv::imshow("triple-pupil", pupil); + cv::waitKey(0); +} + +int CasicSegPostProcess::getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance) { + // mask contours + std::vector> c1; + findContours(con1, c1,cv:: RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c1.size() " << c1.size() << endl; + std::vector c1All; + for (auto c : c1) { + //cout << "c.size()" << c.size() << endl; + c1All.insert(c1All.end(), c.begin(), c.end()); + } + //cout << "c1All.size() " << c1All.size() << endl; + + // other contours + std::vector> c2; + findContours(con2, c2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); + //cout << "c2.size() " << c2.size() << endl; + std::vector c2All; + for (auto c : c2) { + //cout << "c.size()" << c.size() << endl; + c2All.insert(c2All.end(), c.begin(), c.end()); + } + //cout << "c2All.size() " << c2All.size() << endl; + + int distance = 1000; + for (auto p1 : c1All) { + for (auto p2 : c2All) { + int tempDistance = std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); + if (tempDistance < distance) { + distance = tempDistance; + } + if (distance <= chessboardDistance) { + break; + } + } + } + return distance; + + +} + +CasicTriplet CasicSegPostProcess::getMaxTriple(std::vector tripleSet) { + long maxCount = 0; + int maxIndex = -1; + for (int i = 0; i < tripleSet.size(); i++) { + CasicTriplet triplet = tripleSet[i]; + long count = cv::countNonZero(triplet.maskConn) + + cv::countNonZero(triplet.irisConn) + + cv::countNonZero(triplet.pupilConn); + if (count > maxCount) { + maxCount = count; + maxIndex = i; + } + } + return tripleSet[maxIndex]; +} + +std::vector CasicSegPostProcess::leastSquareCircleFitting(std::vector counter) { + + std::vector result; + int center_x, center_y, radius = 0; + + if (counter.size() < 3) { + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; + } + + double sum_x, sum_y = 0.0; + double sum_x2, sum_y2 = 0.0; + double sum_x3, sum_y3 = 0.0; + double sum_xy, sum_x1y2, sum_x2y1 = 0.0; + + for (auto point : counter) { + double x = (double)point.x; + double y = (double)point.y; + double x2 = x * x; + double y2 = y * y; + sum_x += x; + sum_y += y; + sum_x2 += x2; + sum_y2 += y2; + sum_x3 += x2 * x; + sum_y3 += y2 * y; + sum_xy += x * y; + sum_x1y2 += x * y2; + sum_x2y1 += x2 * y; + } + + double N = counter.size(); + double C = N * sum_x2 - sum_x * sum_x; + double D = N * sum_xy - sum_x * sum_y; + double E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x; + double G = N * sum_y2 - sum_y * sum_y; + double H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y; + double a = (H * D - E * G) / (C * G - D * D); + double b = (H * C - E * D) / (D * D - G * C); + double c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N; + + center_x = (int)(a / (-2)); + center_y = (int)(b / (-2)); + radius = (int)(sqrt(a * a + b * b - 4 * c) / 2); + + result.push_back(center_x); + result.push_back(center_y); + result.push_back(radius); + return result; +} + +bool CasicSegPostProcess::postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil,std::vector & irisCircle, std::vector & pupilCircle) { + int chessboard_distance = 15; + + // cv::Mat mask,iris,pupil; + mask.convertTo(mask,CV_8UC1); + iris.convertTo(iris,CV_8UC1); + pupil.convertTo(pupil,CV_8UC1); + + threshold(mask, mask, 127, 255, cv::THRESH_BINARY); + threshold(iris, iris, 90, 255, cv::THRESH_BINARY); + threshold(pupil, pupil, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + morphologyEx(iris, iris, cv::MORPH_CLOSE, element); + +// cv::imshow("mask",mask); +// cv::imshow("iris",iris); +// cv::imshow("pupil",pupil); +// cv::waitKey(0); + +// std::cout << "get connection ..." << std::endl; + + std::vector maskCons = getConnections(mask, 8); + std::vector irisCons = getConnections(iris, 8); + std::vector pupilCons = getConnections(pupil, 8); + + if(maskCons.size() <= 0 || irisCons.size() <= 0 || pupilCons.size() <= 0) + { + return false; + } + +// std::cout << "get triple set ..." << std::endl; + + std::vector tripleSet = getTripleSet(maskCons, irisCons, pupilCons, chessboard_distance); + + if (tripleSet.size() < 1) { + return false; + } + +// std::cout << "get max triple ..." << std::endl; + + CasicTriplet maxTriple = getMaxTriple(tripleSet); + //showTriple(maxTriple); + +// std::cout << "least Square Circle Fitting ..." << std::endl; + + cv::Mat bestIris = maxTriple.irisConn; + std::vector outPoints; + findNonZero(bestIris, outPoints); + irisCircle = leastSquareCircleFitting(outPoints); + int iris_x = irisCircle[0]; + int iris_y = irisCircle[1]; + int iris_r = irisCircle[2]; + + cv::Mat bestPupil = maxTriple.pupilConn; + std::vector> innerCounter; + findContours(bestPupil, innerCounter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + std::vector innerPoints; + for (auto c : innerCounter) { + innerPoints.insert(innerPoints.end(), c.begin(), c.end()); + } + pupilCircle = leastSquareCircleFitting(innerPoints); + int pupil_x = pupilCircle[0]; + int pupil_y = pupilCircle[1]; + int pupil_r = pupilCircle[2]; + + //cout << "iris: " << iris_x << " " << iris_y << " " << iris_r << endl; + //cout << "pupil:" << pupil_x << " " << pupil_y << " " << pupil_r << endl; + +// std::cout << "bitwise_and ..." << std::endl; + + cv::Mat circleMask = cv::Mat::zeros(mask.size(), CV_8UC1); + circle(circleMask,cv::Point(iris_x, iris_y), iris_r, 255, -1); + circle(circleMask,cv::Point(pupil_x, pupil_y), pupil_r, 0, -1); + cv::bitwise_and(mask, circleMask, mask); + + // show result +// cv::imshow("circleMask", circleMask); +// cv::waitKey(0); + + //Mat three_mask = Mat::zeros(mask.rows, mask.cols, CV_8UC3); + //vector channels_m; + //for (int i = 0; i < 3; i++) + //{ + // channels_m.push_back(mask); + //} + //merge(channels_m, three_mask); + + //circle(three_mask, Point(iris_x, iris_y), iris_r, Scalar(0, 0, 255), 1); + //circle(three_mask, Point(pupil_x, pupil_y), pupil_r, Scalar(0, 0, 255), 1); + //cv::imshow("three_mask", three_mask); + //cv::waitKey(0); + return true; +} + +} // end of namespace iristrt diff --git a/casic/iris/CasicSegPostProcess.h b/casic/iris/CasicSegPostProcess.h new file mode 100644 index 0000000..2021ed4 --- /dev/null +++ b/casic/iris/CasicSegPostProcess.h @@ -0,0 +1,37 @@ +#ifndef CASIC_SEG_POST_PROCESS +#define CASIC_SEG_POST_PROCESS + +#include +#include "CasicSegResult.h" + +namespace iristrt +{ + +class CasicSegPostProcess +{ + +public: + + bool postProcess(cv::Mat & mask, cv::Mat & iris, cv::Mat & pupil, std::vector & irisCircle, std::vector & pupilCircle); + +private: + + std::vector getConnections(cv::Mat input, int connectivity); + + void showConnections(std::vector conns); + + std::vector getTripleSet(std::vector maskConns, std::vector irisConns, std:: vector pupilConns, int chessboardDistance); + + void showTriple(CasicTriplet triple); + + int getChessboardDistance(cv::Mat con1, cv::Mat con2, int chessboardDistance); + + CasicTriplet getMaxTriple(std::vector tripleSet); + + std::vector leastSquareCircleFitting(std::vector counter); + +}; // end of class CasicSegPostProcess + +} // end of namespace iristrt + +#endif diff --git a/casic/iris/CasicSegResult.h b/casic/iris/CasicSegResult.h new file mode 100644 index 0000000..7e682eb --- /dev/null +++ b/casic/iris/CasicSegResult.h @@ -0,0 +1,24 @@ +#ifndef CASIC_SEG_RESULT +#define CASIC_SEG_RESULT + +#include "opencv2/opencv.hpp" + +namespace iristrt +{ +struct CasicSegResult +{ + cv::Mat irisMask; + cv::Mat innerMask; + cv::Mat outerCircle; +}; //end of struct CasicSegResult + +struct CasicTriplet +{ + cv::Mat maskConn; + cv::Mat irisConn; + cv::Mat pupilConn; +}; + +} //end of namespace iristrt + +#endif diff --git a/casic/iris/casicIris.pri b/casic/iris/casicIris.pri index d0942ce..43a1bd3 100644 --- a/casic/iris/casicIris.pri +++ b/casic/iris/casicIris.pri @@ -1,4 +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/device/iris/CasicIrisRecState.cpp b/device/iris/CasicIrisRecState.cpp index b64d747..6d2553e 100644 --- a/device/iris/CasicIrisRecState.cpp +++ b/device/iris/CasicIrisRecState.cpp @@ -1,4 +1,8 @@ -#include "CasicIrisRecState.h" +#ifdef _MSC_VER +#pragma execution_character_set("utf-8") // Qt VS 中文兼容(UTF-8) +#endif + +#include "CasicIrisRecState.h" #include "utils/UtilInclude.h" CasicIrisRecState::CasicIrisRecState() @@ -50,14 +54,14 @@ QJsonObject eyeObj; eyeObj.insert("hasEye", irisInfo->hasEye); - QJsonArray rectArray; - rectArray.append(irisInfo->eyeRect.x); - rectArray.append(irisInfo->eyeRect.y); - rectArray.append(irisInfo->eyeRect.width); - rectArray.append(irisInfo->eyeRect.height); - eyeObj.insert("faceRect", rectArray); +// QJsonArray rectArray; +// rectArray.append(irisInfo->eyeRect.x); +// rectArray.append(irisInfo->eyeRect.y); +// rectArray.append(irisInfo->eyeRect.width); +// rectArray.append(irisInfo->eyeRect.height); +// eyeObj.insert("faceRect", rectArray); - obj.insert("eye", eyeObj); +// obj.insert("eye", eyeObj); return obj; } diff --git a/device/iris/IrisRecogProcess.cpp b/device/iris/IrisRecogProcess.cpp index 6869ece..08c8227 100644 --- a/device/iris/IrisRecogProcess.cpp +++ b/device/iris/IrisRecogProcess.cpp @@ -11,11 +11,11 @@ this->exit = false; // 连接算法服务 -// clientUtil = new SocketClientUtil(this); -// clientUtil->connect("127.0.0.1", 2015); + clientUtil = new SocketClientUtil(this); + clientUtil->connect("192.168.83.223", 50007); // 调用socket发送消息 -// connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); + connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData); } IrisRecogProcess::~IrisRecogProcess() @@ -64,9 +64,6 @@ continue; } -// LOG(TRACE) << QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString(); -// LOG_TRACE(QString("[IrisRecogProcess] 准备取出虹膜图像[%1]").arg(ProMemory::getInstance().isIrisQueueEmpty()).toStdString()); - // 取出虹膜图像栈中的一条数据 CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris(); @@ -75,7 +72,7 @@ qDebug() << QString("[IrisRecogProcess] 取出虹膜图像"); // 调用找眼分类器算法 - casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(360); + casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(200); irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo); // 没有找到眼睛则返回 @@ -104,34 +101,107 @@ CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE; - CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; - CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; - CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; - CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); +// CasicIrisRecState::getInstance().irisInfo->hasEye = irisInfo.hasEye; +// CasicIrisRecState::getInstance().irisInfo->eyeRect = irisInfo.eyeRect; +// CasicIrisRecState::getInstance().irisInfo->matData = irisInfo.matData; +// CasicIrisRecState::getInstance().irisInfo->data = ImageUtil::MatImageToQImage(irisInfo.matData); + +// irisInfo.matData = cv::imread("d:\\irisLogs\\photo\\left.bmp", 0); QElapsedTimer timer; timer.start(); - QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * SettingConfig::getInstance().IRIS_HEIGHT); - data.prepend("[-m]"); // 识别 + // 进行预处理 如图像 + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisPreProcess(irisInfo); + + QByteArray data((char*)irisInfo.matData.data, SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 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(); + + 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 < 320 * 240) + recvPupil[i] = clientUtil->getResponse().at(i); + else if (i >= 320 * 240 && i < 2 * 320 * 240) + recvMask[i - 320 * 240] = clientUtil->getResponse().at(i); + else + recvIris[i - 2 * 320 * 240] = clientUtil->getResponse().at(i); + } + + cv::Mat pupil(240, 320, CV_8UC1, recvPupil); + cv::Mat mask(240, 320, CV_8UC1, recvMask); + cv::Mat iris(240, 320, CV_8UC1, recvIris); + irisInfo.segResult.innerMask = pupil; + irisInfo.segResult.irisMask = mask; + irisInfo.segResult.outerCircle = iris; +/* + std::string pupilSeg = QString("%1\\%2\\%3-pupil-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskSeg = QString("%1\\%2\\%3-mask-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string irisSeg = QString("%1\\%2\\%3-iris-seg.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(pupilSeg, pupil); + cv::imwrite(maskSeg, mask); + cv::imwrite(irisSeg, iris); +*/ + qDebug() << QString("算法分割图像成功[%1 ms]").arg(timer.elapsed()); + + // 分割后的后处理 + if(cv::countNonZero(mask) == 0 || cv::countNonZero(pupil) == 0|| cv::countNonZero(iris) == 0) + { + qDebug() << "算法分割图像失败,暂停200ms"; + + addOneTryCount(); + this->msleep(200); // 200ms后再判断 + continue; + } + // 状态修改为特征提取 CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT; - QTimer toTimer; - QEventLoop loop; - toTimer.singleShot(3000, &loop, &QEventLoop::quit); -// connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit); - loop.exec(); + timer.restart(); + qDebug() << "开始提取特征"; + irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo); +/* + std::string codeFilename = QString("%1\\%2\\%3-code.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + std::string maskFilename = QString("%1\\%2\\%3-mask.bmp").arg("d:\\irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString(); + cv::imwrite(codeFilename, irisInfo.irisCode); + cv::imwrite(maskFilename, irisInfo.maskNorm); +*/ + qDebug() << QString("虹膜图像编码成功[%1 ms]").arg(timer.elapsed()); -// QByteArray matchResponse = clientUtil->getResponse(); - QByteArray matchResponse; + // 开始匹配 + for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++) + { + CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i); + float score = casic::iris::CasicIrisInterface::getInstance().calculateMatchScore(feature, irisInfo); + qDebug() << score; + if (score <= 0.32) { + // 找到匹配结果 + emit findMatchedIris(feature.personId); + + break; + } + } + + // 没有匹配上 + addOneTryCount(); + +// QByteArray matchResponse; // LOG(INFO) << QString("算法匹配结果").toStdString() << matchResponse.toStdString(); // LOG_INFO("算法匹配结果 {}",matchResponse.toStdString()); - +/* // 算法调用返回成功 if (matchResponse.size() > 0) { @@ -172,7 +242,7 @@ { // 超时返回则尝试次数+1 addOneTryCount(); - } + }*/ } } @@ -213,11 +283,11 @@ // ProMemory::getInstance().irisCam->stopCapture(); ProMemory::getInstance().clearIrisQueue(); - if (qrand() % 10 < 5) { +// if (qrand() % 10 < 5) { emit failedMatchedIris(); - } - else { - emit findMatchedIris("1"); - } +// } +// else { +// emit findMatchedIris("1"); +// } } } diff --git a/device/iris/IrisRecogProcess.h b/device/iris/IrisRecogProcess.h index c4fc648..76ef115 100644 --- a/device/iris/IrisRecogProcess.h +++ b/device/iris/IrisRecogProcess.h @@ -32,7 +32,7 @@ volatile bool exit; private: -// SocketClientUtil * clientUtil; + SocketClientUtil * clientUtil; void afterRecogAction(); 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/SocketClientUtil.cpp b/utils/SocketClientUtil.cpp new file mode 100644 index 0000000..d4e9c5c --- /dev/null +++ b/utils/SocketClientUtil.cpp @@ -0,0 +1,41 @@ +#include "SocketClientUtil.h" +#include "utils/ByteUtil.h" + +SocketClientUtil::SocketClientUtil(QObject *parent) : QObject(parent) +{ + QObject::connect(&objClient, &QTcpSocket::readyRead, this, &SocketClientUtil::readData); +} + +void SocketClientUtil::connect(QString host, int port) +{ + this->host = host; + this->port = port; + + objClient.connectToHost(this->host, this->port); +} + +void SocketClientUtil::closeConnect() +{ + objClient.close(); +} + +QByteArray SocketClientUtil::getResponse() +{ + return this->response; +} + +void SocketClientUtil::sendData(QByteArray data) +{ +// data.append(0x0D).append(0x0A); // 自动加\r\n标识一帧结束 + objClient.write(data); +} + +void SocketClientUtil::readData() +{ + QByteArray buffer = objClient.readAll(); + response.append(buffer); + + if (response.size() >= 320 * 240 * 3) { + emit this->responseReaded(); + } +} diff --git a/utils/SocketClientUtil.h b/utils/SocketClientUtil.h new file mode 100644 index 0000000..2c833e1 --- /dev/null +++ b/utils/SocketClientUtil.h @@ -0,0 +1,35 @@ +#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(); + +private: + QTcpSocket objClient; + QByteArray response; + + QString host = "127.0.0.1"; + int port = 2015; + +signals: + void responseReaded(); + +public slots: + void readData(); + void sendData(QByteArray data); + +}; + +#endif // QSOCKETCLIENTUTIL_H diff --git a/utils/UtilInclude.h b/utils/UtilInclude.h index d0c934c..b5e2ce9 100644 --- a/utils/UtilInclude.h +++ b/utils/UtilInclude.h @@ -2,10 +2,11 @@ #define UTILINCLUDE_H #include -//#include "ByteUtil.h" +#include "ByteUtil.h" #include "ImageUtil.h" //#include "LogUtil.h" #include "SettingConfig.h" +#include "SocketClientUtil.h" #include "SpeakerUtil.h" #include "TimeCounterUtil.h" //#include "UDPClientUtil.h" diff --git a/utils/utils.pri b/utils/utils.pri index f087542..f72ccb5 100644 --- a/utils/utils.pri +++ b/utils/utils.pri @@ -20,16 +20,15 @@ HEADERS += $$PWD/SpeakerUtil.h SOURCES += $$PWD/SpeakerUtil.cpp -#HEADERS += $$PWD/QSocketClientUtil.h -#SOURCES += $$PWD/QSocketClientUtil.cpp - +HEADERS += $$PWD/SocketClientUtil.h +SOURCES += $$PWD/SocketClientUtil.cpp #HEADERS += $$PWD/SelectDeptUtil.h #SOURCES += $$PWD/SelectDeptUtil.cpp -#HEADERS += $$PWD/QByteUtil.h +HEADERS += $$PWD/ByteUtil.h #HEADERS += $$PWD/TimerCounter.h -#SOURCES += $$PWD/QByteUtil.cpp +SOURCES += $$PWD/ByteUtil.cpp #SOURCES += $$PWD/TimerCounter.cpp