Newer
Older
CasicIrisIdentify / device / iris / IrisRecogProcess.cpp
#ifdef _MSC_VER
#pragma execution_character_set("utf-8")    // Qt VS 中文兼容(UTF-8)
#endif

#include "IrisRecogProcess.h"

IrisRecogProcess::IrisRecogProcess(QObject *parent) : QThread(parent)
{
    this->working = false;
    this->exit = false;

    // 连接算法服务
    clientUtil = new SocketClientUtil(this);
    clientUtil->connect(SettingConfig::getInstance().DEBUG_SEGMENT_IP, 50007);

    // 调用socket发送消息
    connect(this, &IrisRecogProcess::sendDataToExract, clientUtil, &SocketClientUtil::sendData);
}

IrisRecogProcess::~IrisRecogProcess()
{
    this->setWorking(false);
}

void IrisRecogProcess::setWorking(bool working)
{
    this->working = working;
}

void IrisRecogProcess::exitThread()
{
    this->exit = true;
}

void IrisRecogProcess::run()
{
    while (exit == false)
    {
        // 如果工作标志位为否, 则跳出
        if (this->working == false)
        {
            this->msleep(THREAD_MSLEEP); // 200ms后再判断
            continue;
        }

        // 1. 识别状态不等于未开始, 表示已经进入识别程序了, 后续不再执行, 等待本次识别过程结束
        if (CasicIrisRecState::getInstance().state != CasicIrisRecState::IrisRecStateName::REC_NOT_START)
        {
            qDebug() << QString("[IrisRecogProcess] 已在一次识别过程中, 暂停200ms。当前状态为: %1").arg(CasicIrisRecState::getInstance().state);
            this->msleep(THREAD_MSLEEP); // 200ms后再判断
            continue;
        }

        // 2. 图像栈中没有图像则直接返回
        if (ProMemory::getInstance().isIrisQueueEmpty() == true)
        {
            qDebug() << QString("[IrisRecogProcess] 虹膜图像栈空, 暂停200ms");
            this->msleep(THREAD_MSLEEP); // 200ms后再判断
            continue;
        }

        // 3. 取出虹膜图像栈中的一条数据
        CasicIrisInfo irisInfo = ProMemory::getInstance().popCasicIris();

        QElapsedTimer timer;
        timer.start();

        // 4. 调用找眼分类器算法  第一阶段
        casic::iris::CasicIrisInterface::getInstance().setMinEyeSize(SettingConfig::getInstance().MIN_EYE_SIZE);
        irisInfo = casic::iris::CasicIrisInterface::getInstance().findAndCutEye(irisInfo);

        // 找眼计时
        int findEyeLast = timer.elapsed();
        qDebug() << QString("[IrisRecogProcess] 找眼及裁剪[%1 ms]").arg(findEyeLast);

//        CasicIrisInfo irisInfo;
//        irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0);
//        irisInfo.hasEye = true;

        // 4.1 没有找到眼睛则返回
        if (irisInfo.hasEye == false)
        {
            // 表示本次识别结束, 可以开始下一次识别过程
            qDebug() << QString("[IrisRecogProcess] 分类器没有找到眼睛,暂停200ms[%1]").arg(CasicIrisRecState::getInstance().noEyeCount);

            addOneNoEyeCount();
            this->msleep(THREAD_MSLEEP); // 200ms后再判断
            continue;
        }

        // 5. 找到眼睛则开始识别

        // 发送信号更新界面
        emit startIdentifyIris();

        // 调用远程算法进行编码
        if (CasicIrisRecState::getInstance().recoginzeId == "0" || CasicIrisRecState::getInstance().recoginzeId == "")
        {
            // 第一次找到眼则 虹膜识别状态初始化
            CasicIrisRecState::getInstance().initRecognize();
        }

        CasicIrisRecState::getInstance().noEyeCount = 0; // 持续未找到眼的次数清零
        CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FIND_EYE;

        LOG_DEBUG(QString("[IrisRecogProcess step.1] 找眼及裁剪成功[%1 ms]").arg(findEyeLast).toStdString());
        CasicIrisRecState::getInstance().findEyeTmLast = findEyeLast;
        CasicIrisRecState::getInstance().recogTimeLast += findEyeLast;

        // 6. 分割图像
        timer.restart();

        // 进行预处理 转换成 320 * 240 的三通道图像 送去分割
        cv::Mat segMat;

        // 图像转换为RGB的彩色图像
        cv::cvtColor(irisInfo.matData, segMat, cv::COLOR_GRAY2RGB);

        // 缩放到320 * 240
        cv::resize(segMat, segMat, cv::Size(SettingConfig::getInstance().IRIS_WIDTH * 0.5, SettingConfig::getInstance().IRIS_HEIGHT * 0.5));

        // save segMat for debug
        std::string segFilename;
        if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) {
            segFilename = QString("%1/%2/%3-seg.bmp").arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH).arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString();
            cv::imwrite(segFilename, segMat);
        }

//        QElapsedTimer timer;
//        timer.start();
//        CasicIrisInfo irisInfo;
//        irisInfo.matData = cv::imread("/home/casic/code/irisLogs/20231125/200630.951-0.324786.bmp", 0);
//        irisInfo.hasEye = true;
//        cv::Mat segMat = cv::imread("/home/casic/code/CasicIrisIdentify/crash-test/113429-seg.bmp");

        int segMatSize = segMat.cols * segMat.rows;
        QByteArray data((char*)segMat.data, segMatSize * 3); // 三通道

        // 发送TCP消息去匹配
        emit sendDataToExract(data);

        // 等待接收算法返回的结果
        QTimer toTimer;
        QEventLoop loop;
        toTimer.singleShot(3000, &loop, &QEventLoop::quit); // 等待3秒后退出等待
        connect(clientUtil, &SocketClientUtil::responseReaded, &loop, &QEventLoop::quit);
        loop.exec();

        if (clientUtil->getResponse().size() < SettingConfig::getInstance().IRIS_WIDTH * 0.5 * SettingConfig::getInstance().IRIS_HEIGHT * 0.5 * 3) {
            addOneTryCount();
            qDebug() << QString("算法分割图像失败,暂停200ms[%1 ms]").arg(timer.elapsed());
            this->msleep(THREAD_MSLEEP); // 200ms后再判断
            continue;
        }

        char recvPupil[320 * 240 * 1];
        char recvMask[320 * 240 * 1];
        char recvIris[320 * 240 * 1];
        memset(recvPupil, 0, sizeof(recvPupil));
        memset(recvMask, 0, sizeof (recvMask));
        memset(recvIris, 0, sizeof (recvIris));
        for (int i = 0; i < clientUtil->getResponse().size(); i++) {
            if (i < segMatSize)
                recvPupil[i] = clientUtil->getResponse().at(i);
            else if (i >= segMatSize && i < 2 * segMatSize)
                recvMask[i - segMatSize] = clientUtil->getResponse().at(i);
            else
                recvIris[i - 2 * segMatSize] = clientUtil->getResponse().at(i);
        }

        cv::Mat pupil(segMat.rows, segMat.cols, CV_8UC1, recvPupil);
        cv::Mat mask(segMat.rows, segMat.cols, CV_8UC1, recvMask);
        cv::Mat iris(segMat.rows, segMat.cols, CV_8UC1, recvIris);
        irisInfo.segResult.innerMask = pupil;
        irisInfo.segResult.irisMask = mask;
        irisInfo.segResult.outerCircle = iris;

        // 成功接收后清除缓存区
        clientUtil->resetRecvBuffer();
/*
        std::string pupilSeg = QString("%1/%2/%3-pupil-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString();
        std::string maskSeg = QString("%1/%2/%3-mask-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString();
        std::string irisSeg = QString("%1/%2/%3-iris-seg.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss")).toStdString();
        cv::imwrite(pupilSeg, pupil);
        cv::imwrite(maskSeg, mask);
        cv::imwrite(irisSeg, iris);
*/
        // 第二阶段 分割图像计时
        int segLast = timer.elapsed();
        qDebug() << QString("[IrisRecogProcess] 算法分割图像成功[%1 ms]").arg(segLast);
        LOG_DEBUG(QString("[IrisRecogProcess step.2] 算法分割图像成功[%1 ms]").arg(segLast).toStdString());
        CasicIrisRecState::getInstance().segmentTmLast = segLast;
        CasicIrisRecState::getInstance().recogTimeLast += segLast;

        // 7. 分割后的后处理 编码及提取特征值
        timer.restart();
        if (cv::countNonZero(mask) == 0  ||  cv::countNonZero(pupil) == 0 || cv::countNonZero(iris) == 0)
        {
            qDebug() << "算法分割图像失败,暂停200ms";

            addOneTryCount();
            this->msleep(THREAD_MSLEEP); // 200ms后再判断
            continue;
        }

        // 状态修改为特征提取
        CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_FEATURE_EXTRACT;
        irisInfo = casic::iris::CasicIrisInterface::getInstance().irisEncode(irisInfo);
/*
        std::string codeFilename = QString("%1/%2/%3-code.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString();
        std::string maskFilename = QString("%1/%2/%3-mask.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString();
        cv::imwrite(codeFilename, irisInfo.irisCode);
        cv::imwrite(maskFilename, irisInfo.maskNorm);
*/

        if (irisInfo.postProcSucc == false) {
            qDebug() << "算法post process失败,暂停200ms";

            addOneTryCount();
            this->msleep(THREAD_MSLEEP); // 200ms后再判断
            continue;
        }

        // 从编码中提取特征值
        irisInfo.irisFeatureCode = casic::iris::CasicIrisInterface::getInstance().extractFeature(irisInfo);

        int encodeLast = timer.elapsed();
        qDebug() << QString("[IrisRecogProcess] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast);
        LOG_DEBUG(QString("[IrisRecogProcess step.3] 虹膜图像编码并提取特征值成功[%1 ms]").arg(encodeLast).toStdString());
        CasicIrisRecState::getInstance().extractTmLast = encodeLast;
        CasicIrisRecState::getInstance().recogTimeLast += encodeLast;

//        cv::imwrite(QString("%1/%2/%3.bmp").arg("/home/nvidia/irisLogs").arg(QDate::currentDate().toString("yyyyMMdd")).arg(QTime::currentTime().toString("HHmmss.zzz")).toStdString(), irisInfo.matData);

        // 开始匹配
        timer.restart();
        float minScore = 1.0f;
        for (int i = 0; i < ProMemory::getInstance().getIrisFeatures().size(); i++)
        {
            CasicIrisFeature feature = ProMemory::getInstance().getIrisFeatures().at(i);

            float score = casic::iris::CasicIrisInterface::getInstance().calculatePairPoints(feature.irisFeatureCode, irisInfo.irisFeatureCode); // 计算压缩编码的比较值
            if (score < minScore) {
                minScore = score;
            }

            if (score <= 0.32) {
                CasicIrisRecState::getInstance().state = CasicIrisRecState::REC_SEARCH_SUCC;
                CasicIrisRecState::getInstance().matchedId = feature.personId;
                CasicIrisRecState::getInstance().score = score;
                CasicIrisRecState::getInstance().timeStampSucc = QDateTime::currentMSecsSinceEpoch();

                break ;
            }
        }

        int matchLast = timer.elapsed();
        qDebug() << QString("[IrisRecogProcess] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state);
        LOG_DEBUG(QString("[IrisRecogProcess step.4] 虹膜库匹配[%1 ms][%2]").arg(matchLast).arg(CasicIrisRecState::getInstance().state).toStdString());
        CasicIrisRecState::getInstance().matchTmLast = matchLast;
        CasicIrisRecState::getInstance().recogTimeLast += matchLast;

        if (SettingConfig::getInstance().DEBUG_SAVE_IMAGE == true) {
            QFile::remove(QString::fromStdString(segFilename)); // delete segment file
            cv::imwrite(QString("%1/%2/%3-%4.bmp")
                        .arg(SettingConfig::getInstance().DEBUG_IMAGE_BASE_PATH)
                        .arg(QDate::currentDate().toString("yyyyMMdd"))
                        .arg(QTime::currentTime().toString("HHmmss.zzz"))
                        .arg(minScore).toStdString(), irisInfo.matData);
        }

        if (CasicIrisRecState::getInstance().state == CasicIrisRecState::REC_SEARCH_SUCC) {
            // 找到匹配结果
            afterRecogAction();

            emit findMatchedIris(CasicIrisRecState::getInstance().matchedId);
        } else {
            // 没有匹配上
            addOneTryCount();
        }
    }
}

void IrisRecogProcess::afterRecogAction()
{
    this->setWorking(false);
    ProMemory::getInstance().irisCamCtrl->stopCapture();
    ProMemory::getInstance().clearIrisQueue(); // 清理虹膜数据栈
}

void IrisRecogProcess::addOneTryCount()
{
    CasicIrisRecState::getInstance().tryCount++;
//    LOG(DEBUG) << QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString();
//    LOG_DEBUG(QString("[CasicIrisRecogProcess]已尝试次数[%1]").arg(CasicIrisRecState::getInstance().tryCount).toStdString());

    CasicIrisRecState::getInstance().state = CasicIrisRecState::IrisRecStateName::REC_NOT_START;
    if (CasicIrisRecState::getInstance().tryCount >= SettingConfig::getInstance().MAX_IRIS_TRY_COUNT)
    {
        CasicIrisRecState::getInstance().noEyeCount = 0;
        CasicIrisRecState::getInstance().tryCount = 0;

        this->setWorking(false);
        ProMemory::getInstance().irisCamCtrl->stopCapture();
        ProMemory::getInstance().clearIrisQueue();

        emit failedMatchedIris();
    }
}
void IrisRecogProcess::addOneNoEyeCount()
{
    if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_IDENTIFYING)
    {
        CasicIrisRecState::getInstance().noEyeCount++;

        // 如果在识别中 则超过一定次数后返回识别失败
        if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_EYE_NOT_FOUND_COUNT)
        {
            // 发送信号识别失败
            emit failedMatchedIris();

            CasicIrisRecState::getInstance().noEyeCount = 0;

            // 识别失败时暂停工作 待返回识别界面时再开始工作和拍图
            this->setWorking(false);
            ProMemory::getInstance().irisCamCtrl->stopCapture();
            ProMemory::getInstance().clearIrisQueue();
        }
    } else if (ProMemory::getInstance().appState == AppConstants::ApplicationState::STATE_WORKING)
    {
        CasicIrisRecState::getInstance().noEyeCount++;

        // 如果在工作状态 则超过一定次数后返回待机
        if (CasicIrisRecState::getInstance().noEyeCount >= SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT &&
                SettingConfig::getInstance().MAX_FACE_NOT_FOUND_COUNT > 0)
        {
            // 发送信号待机
            emit backToLockScreen();

            CasicIrisRecState::getInstance().noEyeCount = 0;

            // 待机界面还在拍图和工作
            ProMemory::getInstance().clearIrisQueue();
        }
    } else {
        // 待机界面 不做处理 等待找到眼
    }
}