Bläddra i källkod

人脸识别模块修改

wangyaojie 2 år sedan
förälder
incheckning
2bada37893

+ 5 - 4
client/answerWidget.cpp

@@ -65,8 +65,8 @@ answerWidget::answerWidget(QWidget *parent) :
     connect(g_httpBllPtr.get(), &CHttpBll::sgnSubmitQuestionAnswer, this, &answerWidget::onSubmitQuestionAnswer);
 	qRegisterMetaType<CEndExam>("CEndExam");
 	connect(g_httpBllPtr.get(), &CHttpBll::sgnEndExam, this, &answerWidget::onEndExam);
-    qRegisterMetaType<CGetOrgPropertiesByGroupWithoutCache>("CGetOrgPropertiesByGroupWithoutCache");
-    connect(g_httpBllPtr.get(), &CHttpBll::sgnGetOrgPropertiesByGroupWithoutCache, this, &answerWidget::onGetOrgPropertiesByGroupWithoutCache);
+//    qRegisterMetaType<CGetOrgPropertiesByGroupWithoutCache>("CGetOrgPropertiesByGroupWithoutCache");
+//    connect(g_httpBllPtr.get(), &CHttpBll::sgnGetOrgPropertiesByGroupWithoutCache, this, &answerWidget::onGetOrgPropertiesByGroupWithoutCache);
     qRegisterMetaType<CStartFaceLiveVerify>("CStartFaceLiveVerify");
     connect(g_httpBllPtr.get(), &CHttpBll::sgnStartFaceLiveVerify, this, &answerWidget::onStartFaceLiveVerify);
 	
@@ -171,7 +171,7 @@ answerWidget::answerWidget(QWidget *parent) :
                 if(m_pFaceLiveness == nullptr)
                 {
                     stopCapture();
-                    m_pFaceLiveness = std::make_shared<faceLiveness>(g_appInfoPtr->m_pAnsBgWidget);
+                    m_pFaceLiveness = std::make_shared<faceLiveness>(FACE_LIVENESS_TYPE::flt_inprogress, g_appInfoPtr->m_pAnsBgWidget);
                     connect(m_pFaceLiveness.get(), &faceLiveness::faceLivenessFaild, this, [&](){
                         g_appInfoPtr->m_oExamInfo.nFaceLiveVerifyId = 0;
                         m_pFaceLiveness.reset();
@@ -1070,7 +1070,8 @@ void answerWidget::onStartAnswer(CStartAnswer startAnswer)
 		//获取活体参数
 		if (g_appInfoPtr->m_oExamInfo.bLivenessCheck)
 		{
-            GetOrgPropertiesByGroupWithoutCache();
+            StartFaceLiveVerify();
+//            GetOrgPropertiesByGroupWithoutCache();
 //			CHttpRequestPackage hrp;
 //			hrp.sUri = QString("/api/ecs_core/org/getOrgPropertiesByGroupWithoutCache/%1/config4Edit1")
 //				.arg(g_appInfoPtr->m_sRootOrgId);

+ 148 - 42
client/courseList.cpp

@@ -44,7 +44,9 @@ courseList::courseList(QWidget *parent) :
 	connect(g_httpBllPtr.get(), &CHttpBll::sgnLivenessEnabled, this, &courseList::onLivenessEnabled);
     qRegisterMetaType<CEndExam>("CEndExam");
     connect(g_httpBllPtr.get(), &CHttpBll::sgnEndExam, this, &courseList::onEndExam);
-	
+    qRegisterMetaType<CGetOrgPropertiesByGroupWithoutCache>("CGetOrgPropertiesByGroupWithoutCache");
+    connect(g_httpBllPtr.get(), &CHttpBll::sgnGetOrgPropertiesByGroupWithoutCache, this, &courseList::onGetOrgPropertiesByGroupWithoutCache);
+
 
 	sqlite3_stmt *stmt;
 	QString sSql = QString("select agreement from t_agreement where student_id=%1")
@@ -536,9 +538,9 @@ void courseList::onIpLimit(CIpLimit ipLimit)
 
 void courseList::onGetExamProperty(CGetExamProperty getExamProperty)
 {
-	if (getExamProperty.nCode == 200)
+    if (getExamProperty.nCode == 200)
 	{
-        if (getExamProperty.sType == "CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE")
+        if (getExamProperty.sType == "CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE,IS_FACE_VERIFY_BEFORE")
 		{
             g_appInfoPtr->m_oExamInfo.bIsFaceCheck = getExamProperty.bIsFaceCheck;
             g_appInfoPtr->m_oExamInfo.nWarnThreshold = getExamProperty.nWarnThreshold;
@@ -546,6 +548,7 @@ void courseList::onGetExamProperty(CGetExamProperty getExamProperty)
             g_appInfoPtr->m_oExamInfo.nFaceVerifyStartMinute = getExamProperty.nFaceVerifyStartMinute;
             g_appInfoPtr->m_oExamInfo.nFaceVerifyEndMinute = getExamProperty.nFaceVerifyEndMinute;
             g_appInfoPtr->m_oExamInfo.bIsStrangerEnable = getExamProperty.bIsStrangerEnable;
+            g_appInfoPtr->m_oExamInfo.bIsLivenessBefore = getExamProperty.bIsLivenessBefore;
             if (getExamProperty.bCheckEnvironment && !g_appInfoPtr->m_oExamInfo.bIsExamInProgress)
 			{
                 if(m_pWhetherEnvTest == nullptr)
@@ -584,13 +587,13 @@ void courseList::onGetExamProperty(CGetExamProperty getExamProperty)
 
 			}
 			else
-			{
-				//人脸识别
-				CHttpRequestPackage hrp;
-				hrp.sUri = QString("/api/ecs_exam_work/exam/faceCheckEnabled/%1")
-					.arg(g_appInfoPtr->m_oExamInfo.nExamId);
-				hrp.nRequestType = RequestType::rtFaceCheckEnabled;
-				g_httpBllPtr->get(hrp);
+            {
+                //人脸识别
+                CHttpRequestPackage hrp;
+                hrp.sUri = QString("/api/ecs_exam_work/exam/faceCheckEnabled/%1")
+                    .arg(g_appInfoPtr->m_oExamInfo.nExamId);
+                hrp.nRequestType = RequestType::rtFaceCheckEnabled;
+                g_httpBllPtr->get(hrp);
 			}
 		}
 	}
@@ -613,33 +616,33 @@ void courseList::onFaceCheckEnabled(CFaceCheckEnabled faceCheckEnabled)
 	{
         //人脸检测
         g_appInfoPtr->m_oExamInfo.bFaceCheck = faceCheckEnabled.bEnabled;
-        if (faceCheckEnabled.bEnabled && !g_appInfoPtr->m_oExamInfo.bIsExamInProgress)
-		{			 
-			if (g_appInfoPtr->m_sStudentPhotoPath.isEmpty())
-			{
-                ShowMsg(QString::fromLocal8Bit("本场考试需要进行人脸检测,但是您没有上传底照,请联系老师"), this, MSG_ICON_TYPE::mit_error);
-				return;
-			}
-			if (m_pFaceCompare == nullptr)
-			{
-				m_pFaceCompare = std::make_shared<faceCompare>(this);
-				connect(m_pFaceCompare.get(), &faceCompare::exitFaceCompare, this, [&]() {
-					m_pFaceCompare.reset();
-				});
-				connect(m_pFaceCompare.get(), &faceCompare::faceComparePass, this, [&]() {
-					m_pFaceCompare.reset();
-                    //活体检测
-                    CHttpRequestPackage hrp;
-                    hrp.sUri = QString("/api/ecs_exam_work/exam/identificationOfLivingEnabled/%1")
-                        .arg(g_appInfoPtr->m_oExamInfo.nExamId);
-                    hrp.nRequestType = RequestType::rtLivenessEnabled;
-                    g_httpBllPtr->get(hrp);
-				});
-
-			}
-			m_pFaceCompare->show();
-		}
-		else
+//        if (faceCheckEnabled.bEnabled && !g_appInfoPtr->m_oExamInfo.bIsExamInProgress)
+//		{
+//			if (g_appInfoPtr->m_sStudentPhotoPath.isEmpty())
+//			{
+//                ShowMsg(QString::fromLocal8Bit("本场考试需要进行人脸检测,但是您没有上传底照,请联系老师"), this, MSG_ICON_TYPE::mit_error);
+//				return;
+//			}
+//			if (m_pFaceCompare == nullptr)
+//			{
+//				m_pFaceCompare = std::make_shared<faceCompare>(this);
+//				connect(m_pFaceCompare.get(), &faceCompare::exitFaceCompare, this, [&]() {
+//					m_pFaceCompare.reset();
+//				});
+//				connect(m_pFaceCompare.get(), &faceCompare::faceComparePass, this, [&]() {
+//					m_pFaceCompare.reset();
+//                    //活体检测
+//                    CHttpRequestPackage hrp;
+//                    hrp.sUri = QString("/api/ecs_exam_work/exam/identificationOfLivingEnabled/%1")
+//                        .arg(g_appInfoPtr->m_oExamInfo.nExamId);
+//                    hrp.nRequestType = RequestType::rtLivenessEnabled;
+//                    g_httpBllPtr->get(hrp);
+//				});
+
+//			}
+//			m_pFaceCompare->show();
+//		}
+//		else
 		{
 			//活体检测
 			CHttpRequestPackage hrp;
@@ -662,14 +665,42 @@ void courseList::onFaceCheckEnabled(CFaceCheckEnabled faceCheckEnabled)
 	}
 }
 
+void courseList::doFaceCompare()
+{
+    if (g_appInfoPtr->m_sStudentPhotoPath.isEmpty())
+    {
+        ShowMsg(QString::fromLocal8Bit("本场考试需要进行人脸检测,但是您没有上传底照,请联系老师"), this, MSG_ICON_TYPE::mit_error);
+        return;
+    }
+
+    if (m_pFaceCompare == nullptr)
+    {
+        m_pFaceCompare = std::make_shared<faceCompare>(this);
+        connect(m_pFaceCompare.get(), &faceCompare::exitFaceCompare, this, [&]() {
+            m_pFaceCompare.reset();
+        });
+        connect(m_pFaceCompare.get(), &faceCompare::faceComparePass, this, [&]() {
+            m_pFaceCompare.reset();
+            //进入待考
+            enterWaitExam();
+        });
+
+    }
+    m_pFaceCompare->show();
+
+}
+
 void courseList::onLivenessEnabled(CLivenessEnabled livenessEnabled)
 {
 	if (livenessEnabled.nCode == 200)
 	{
         g_appInfoPtr->m_oExamInfo.bLivenessCheck = livenessEnabled.bEnabled;
 
+        GetOrgPropertiesByGroupWithoutCache();
+
+
         //进入待考
-        enterWaitExam();
+//        enterWaitExam();
 	}
 	else
 	{
@@ -684,6 +715,81 @@ void courseList::onLivenessEnabled(CLivenessEnabled livenessEnabled)
 	}
 }
 
+void courseList::doLiveness()
+{
+    //活体检测
+    if(m_pFaceLiveness == nullptr)
+    {
+        m_pFaceLiveness = std::make_shared<faceLiveness>(FACE_LIVENESS_TYPE::flt_entry_exam, this);
+        connect(m_pFaceLiveness.get(), &faceLiveness::faceLivenessFaild, this, [&](){
+            g_appInfoPtr->m_oExamInfo.nFaceLiveVerifyId = 0;
+            m_pFaceLiveness.reset();
+
+
+        });
+
+        connect(m_pFaceLiveness.get(), &faceLiveness::faceLivenessSucceed, this, [&](){
+            g_appInfoPtr->m_oExamInfo.nFaceLiveVerifyId = 0;
+            m_pFaceLiveness.reset();
+
+            //进入待考
+            enterWaitExam();
+        });
+        m_pFaceLiveness->show();
+    }
+}
+
+void courseList::GetOrgPropertiesByGroupWithoutCache()
+{
+    CHttpRequestPackage hrp;
+    hrp.sUri = QString("/api/ecs_core/org/getOrgPropertiesByGroupWithoutCache/%1/config4Edit1")
+        .arg(g_appInfoPtr->m_sRootOrgId);
+    hrp.nRequestType = RequestType::rtGetOrgPropertiesByGroupWithoutCache;
+    g_httpBllPtr->get(hrp);
+
+}
+
+void courseList::onGetOrgPropertiesByGroupWithoutCache(CGetOrgPropertiesByGroupWithoutCache orgProperties)
+{
+    if(orgProperties.nCode == 200)
+    {
+        m_nRetryCount = 0;
+        g_appInfoPtr->m_oExamInfo.nActionNum = orgProperties.nActionNum;
+        g_appInfoPtr->m_oExamInfo.sActionOptions = orgProperties.sActionOptions;
+        g_appInfoPtr->m_oExamInfo.sActionOrder = orgProperties.sActionOrder;
+        g_appInfoPtr->m_oExamInfo.nActionDuration = orgProperties.nActionDuration;
+        g_appInfoPtr->m_oExamInfo.nActionAlert = orgProperties.nActionAlert;
+        g_appInfoPtr->m_oExamInfo.nAllActionDuration = orgProperties.nAllActionDuration;
+
+        if(!g_appInfoPtr->m_oExamInfo.bIsExamInProgress)
+        {
+            if(g_appInfoPtr->m_oExamInfo.bIsLivenessBefore)
+            {
+                doLiveness();
+            }
+            else if(g_appInfoPtr->m_oExamInfo.bFaceCheck)
+            {
+                doFaceCompare();
+            }
+        }
+		else
+		{
+			enterWaitExam();
+		}
+    }
+    else
+    {
+        if(orgProperties.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取活体信息失败"), this, MSG_ICON_TYPE::mit_error);
+        }
+        else
+        {
+            ShowMsg(orgProperties.sMessage, this, MSG_ICON_TYPE::mit_error);
+        }
+    }
+}
+
 void courseList::enterWaitExam()
 {
 	//进入待考
@@ -1065,9 +1171,9 @@ void courseList::onCheckExamInProgress(CCheckExamInProgress checkExamInProgress)
                 //环境监测
                 CHttpRequestPackage hrp;
                 hrp.sUri = QString("/api/ecs_exam_work/exam/getExamPropertyFromCacheByStudentSession/%1/%2")
-                    .arg(g_appInfoPtr->m_oExamInfo.nExamId).arg("CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE");
+                    .arg(g_appInfoPtr->m_oExamInfo.nExamId).arg("CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE,IS_FACE_VERIFY_BEFORE");
                 hrp.nRequestType = RequestType::rtGetExamProperty;
-                hrp.sCommonStr = "CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE";
+                hrp.sCommonStr = "CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE,IS_FACE_VERIFY_BEFORE";
                 g_httpBllPtr->get(hrp);
 
 			}
@@ -1098,9 +1204,9 @@ void courseList::onCheckExamInProgress(CCheckExamInProgress checkExamInProgress)
                 //环境监测
                 CHttpRequestPackage hrp;
                 hrp.sUri = QString("/api/ecs_exam_work/exam/getExamPropertyFromCacheByStudentSession/%1/%2")
-                    .arg(g_appInfoPtr->m_oExamInfo.nExamId).arg("CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE");
+                    .arg(g_appInfoPtr->m_oExamInfo.nExamId).arg("CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE,IS_FACE_VERIFY_BEFORE");
                 hrp.nRequestType = RequestType::rtGetExamProperty;
-                hrp.sCommonStr = "CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE";
+                hrp.sCommonStr = "CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE,IS_FACE_VERIFY_BEFORE";
                 g_httpBllPtr->get(hrp);
 			}						
 		}		

+ 7 - 0
client/courseList.h

@@ -24,6 +24,8 @@
 #include "clEditPassword.h"
 #include "CHttpBll.h"
 #include "environmentalTest.h"
+#include "faceLiveness.h"
+
 namespace Ui {
 class courseList;
 }
@@ -77,6 +79,7 @@ private slots:
 	void onGetExamProperty(CGetExamProperty getExamProperty);
 	void onFaceCheckEnabled(CFaceCheckEnabled faceCheckEnabled);
 	void onLivenessEnabled(CLivenessEnabled livenessEnabled);
+    void onGetOrgPropertiesByGroupWithoutCache(CGetOrgPropertiesByGroupWithoutCache orgProperties);
 
     void onEndExam(CEndExam endExam);
 private:
@@ -87,6 +90,9 @@ private:
 	void getStudentInfoBySession();
 	void logout();
 	void enterWaitExam();
+    void GetOrgPropertiesByGroupWithoutCache();
+    void doFaceCompare();
+    void doLiveness();
 
     Ui::courseList *ui;
 
@@ -115,6 +121,7 @@ private:
 //    std::shared_ptr<cloeUploadFile> m_pCloeUploadFile;
     std::shared_ptr<awBackground> m_pBackground;
     std::shared_ptr<awResumeExam> m_pResumeExam;
+    std::shared_ptr<faceLiveness> m_pFaceLiveness;
     int m_nRetryCount = 0;
 };
 

+ 1 - 1
client/login.cpp

@@ -99,7 +99,7 @@ login::login(QWidget *parent)
     qss.replace("RATE_BASE_SIZE1", QString::number(RATE_BASE_SIZE1));
     g_appInfoPtr->m_sQssStr = qss;
     setStyleSheet(qss);
-    qDebug()<<qss;
+
 	ui->widget_BG->setVisible(false);
 	setGeometry(0, 0, dekwiget->width(), dekwiget->height());
 	m_pCourseList = nullptr;

+ 3 - 0
common/CAppInfo.h

@@ -30,6 +30,7 @@ public:
     bool bLivenessCheck;    
     int nWarnThreshold;//预警阈值
     bool bIsFaceCheck;//进入考试是否验证人脸识别(强制、非强制)
+    bool bIsLivenessBefore;//考前活体检测
     int nSnapshotInterval;//抓拍间隔时间
     int nFaceVerifyStartMinute;//活体检测开始分钟数
     int nFaceVerifyEndMinute;//活体检测结束分钟数
@@ -42,6 +43,7 @@ public:
     QString sActionOrder;//FIXED
     int nActionDuration;//单个动作最大时长
     int nActionAlert;//指定动作检测提醒N秒后开始检测
+    int nAllActionDuration;//整体动作最大时长
     ExamInfo()
     {
         bIsExamInProgress = false;
@@ -66,6 +68,7 @@ public:
 		sActionOptions = "";
 		sActionOrder = "";
 		nActionDuration = 0;
+        nAllActionDuration = 0;
 		nActionAlert = 0;
 		nFaceLiveVerifyId = 0;
 		nFaceVerifyDoMinute = 0;

+ 6 - 0
common/CHttpBll.cpp

@@ -1572,6 +1572,11 @@ void CHttpBll::genGetExamProperty(CGetExamProperty *pGetExamProperty, const std:
             pGetExamProperty->sCheatingRemark = jsonRoot["CHEATING_REMARK"].asString().c_str();
         }
 
+        if (jsonRoot.isMember("IS_FACE_VERIFY_BEFORE"))
+        {
+            pGetExamProperty->bIsLivenessBefore = QVariant(jsonRoot["IS_FACE_VERIFY_BEFORE"].asString().c_str()).toBool();
+        }
+
         if (jsonRoot.isMember("OFFLINE_UPLOAD_FILE_TYPE"))
         {
             QString sFileType = jsonRoot["OFFLINE_UPLOAD_FILE_TYPE"].asString().c_str();
@@ -2541,6 +2546,7 @@ void CHttpBll::genGetOrgPropertiesByGroupWithoutCache(CGetOrgPropertiesByGroupWi
         pOrgProperties->sActionOrder = jsonRoot["ACTION_ORDER"].asString().c_str();
         pOrgProperties->nActionDuration = QVariant(jsonRoot["ACTION_DURATION"].asString().c_str()).toInt();
         pOrgProperties->nActionAlert = QVariant(jsonRoot["ACTION_ALERT"].asString().c_str()).toInt();
+        pOrgProperties->nAllActionDuration = QVariant(jsonRoot["ALL_ACTION_DURATION"].asString().c_str()).toInt();
     }
     catch (const std::exception &e)
     {

+ 6 - 1
common/httpDataDef.h

@@ -445,6 +445,8 @@ public:
 	QString sCheatingRemark;//作弊说明	
 	std::vector<QString> vOfflineUploadFileType;
 
+    bool bIsLivenessBefore;//考前活体检测
+
 	CGetExamProperty()
 	{
 		sType = "";
@@ -463,6 +465,7 @@ public:
 		bIsObjScoreView = false;
 		bShowCheatingRemark = false;
 		sCheatingRemark = "";
+        bIsLivenessBefore = false;
 	}
 };
 
@@ -1376,7 +1379,8 @@ public:
 	QString sActionOrder;//FIXED
 	int nActionDuration;//单个动作最大时长
 	int nActionAlert;//指定动作检测提醒N秒后开始检测
-	
+    int nAllActionDuration;//整体动作时长
+
 	CGetOrgPropertiesByGroupWithoutCache()
 	{
 		nActionNum = 0;
@@ -1384,6 +1388,7 @@ public:
 		sActionOrder = "";
 		nActionDuration = 0;
 		nActionAlert = 0;
+        nAllActionDuration = 0;
 	}
 };
 

+ 1 - 1
face/CFaceRecProc.cpp

@@ -403,7 +403,7 @@ bool CFaceRecProc::getFaceAttribute(cv::Mat faceMat, int nFaceStatus, bool &bHas
             }
             else if(nFaceStatus == SL_HEAD_DOWN)
             {
-                if(pitch < -20)
+                if(pitch < -10)
                 {
                     bHasStatus = true;
                 }

+ 242 - 70
face/faceLiveness.cpp

@@ -11,9 +11,9 @@
 #include "CCommonTools.h"
 #include "CFaceRecProc.h"
 
-faceLiveness::faceLiveness(QWidget *parent) :
+faceLiveness::faceLiveness(FACE_LIVENESS_TYPE livenessType, QWidget *parent) :
     QWidget(parent),
-    ui(new Ui::faceLiveness)
+    ui(new Ui::faceLiveness), m_livenessType(livenessType)
 {
     ui->setupUi(this);
 
@@ -25,8 +25,18 @@ faceLiveness::faceLiveness(QWidget *parent) :
     qRegisterMetaType<CProcessUpload>("CProcessUpload");
     connect(g_httpBllPtr.get(), &CHttpBll::sgnProcessUpload, this, &faceLiveness::onProcessUpload);
     connect(g_httpBllPtr.get(), &CHttpBll::sgnSaveFaceLiveVerifyResult, this, &faceLiveness::onSaveFaceLiveVerifyResult);
+    connect(g_httpBllPtr.get(), &CHttpBll::sgnSaveFaceCompareResult, this, &faceLiveness::onSaveFaceCompareResult);
 
-    m_nMaxSeconds = 60;
+    if(m_livenessType == FACE_LIVENESS_TYPE::flt_inprogress)
+    {
+        ui->label_fl_time->setVisible(true);
+    }
+    else
+    {
+        ui->label_fl_time->setVisible(false);
+    }
+
+    m_nMaxSeconds = g_appInfoPtr->m_oExamInfo.nAllActionDuration;
 
     QString sFileName = g_appInfoPtr->m_sStudentPhotoPath.right(g_appInfoPtr->m_sStudentPhotoPath.length() - g_appInfoPtr->m_sStudentPhotoPath.lastIndexOf("/") - 1);
     sFileName = g_appInfoPtr->m_sCacheFileDir + sFileName;
@@ -40,26 +50,6 @@ faceLiveness::faceLiveness(QWidget *parent) :
         g_httpBllPtr->downLoad(hrp);
     }
 
-//    m_cam.set(CV_CAP_PROP_FOURCC, CV_FOURCC('M', 'J', 'P', 'G'));
-//    int inWidth = ui->widget_fc_camera->width();
-//    m_cam.set(CV_CAP_PROP_FRAME_WIDTH, inWidth);
-//    int inHeight = ui->widget_fc_camera->height();
-//    m_cam.set(CV_CAP_PROP_FRAME_HEIGHT, inHeight);
-//    m_cam.set(CV_CAP_PROP_FPS, 5);
-
-//    m_pinitTimer = std::make_shared<QTimer>();
-//    m_pinitTimer->setInterval(1000);
-//    connect(m_pinitTimer.get(), &QTimer::timeout, this, [&](){
-//        m_pinitTimer->stop();
-//        if(!m_cam.open(0))
-//        {
-//            ShowMsg(QString::fromLocal8Bit("打开摄像头失败"), this, MSG_ICON_TYPE::mit_error);
-//            return;
-//        }
-
-//        m_pVideoTimer->start();
-//    });
-
     m_pVideoTimer = std::make_shared<QTimer>();
     m_pVideoTimer->setInterval(200);
     connect(m_pVideoTimer.get(), &QTimer::timeout, this, [&](){
@@ -97,7 +87,7 @@ faceLiveness::faceLiveness(QWidget *parent) :
 
     LivenessVerifyInfo lvif;
     lvif.sActionType = ACTION_TYPE::AT_FACE_DETECT;
-    lvif.nActionLeftSceonds = 60;
+    lvif.nActionLeftSceonds = m_nFaceDetectDuration;
     m_livenessList.push_back(lvif);
 	m_currentVerifyInfo = lvif;
     if(g_appInfoPtr->m_oExamInfo.sActionOrder == "FIXED")
@@ -136,6 +126,53 @@ faceLiveness::faceLiveness(QWidget *parent) :
 
     m_bIsRun = true;
     m_thread = std::thread(std::bind(&faceLiveness::threadProc, this));
+
+    m_initTimer = std::make_shared<QTimer>();
+    m_initTimer->setInterval(100);
+    connect(m_initTimer.get(), &QTimer::timeout, this, [&](){
+		m_initTimer->stop();
+        if(g_faceRecProcPtr == nullptr)
+        {
+            g_faceRecProcPtr = std::make_shared<CFaceRecProc>();
+
+            if (!g_appInfoPtr->m_sStudentPhotoPath.isEmpty())
+            {
+                if(!g_faceRecProcPtr->hasBaseImage())
+                {
+                    if(!setBaseImage())
+                    {
+                        return;
+                    }
+                }
+            }
+            else
+            {
+                ShowMsg(QString::fromLocal8Bit("当前考试未底照"), this, MSG_ICON_TYPE::mit_error);
+                return;
+            }
+        }
+
+        if(!g_faceRecProcPtr->hasBaseImage())
+        {
+            if(!setBaseImage())
+            {
+                return;
+            }
+        }
+
+        m_bStartCompare = true;
+        m_fMaxYaw = 0;
+        m_fMinYaw = 0;
+        m_sLivenessStatus = STATUS_TYPE::ST_SUCCESS;
+
+        {
+            std::scoped_lock sl(m_livenessListMutex);
+            m_currentVerifyInfo = m_livenessList[m_nCurIndex];
+        }
+        initAcionIcon();
+        m_ActionTimer->start();
+    });
+    m_initTimer->start();
 }
 
 faceLiveness::~faceLiveness()
@@ -219,6 +256,11 @@ void faceLiveness::initUI()
                                    ui->label_fl_hint->width(), ui->label_fl_hint->height());
 
     ui->widget_fl_action->setVisible(false);
+
+    ui->btn_fl_startVerify->setVisible(false);
+    ui->label_fl_tips->setVisible(false);
+    ui->widget_fc_camera->setFixedHeight(g_appInfoPtr->m_fRate*431);
+    ui->widget_fl_action->setVisible(true);
 }
 
 void faceLiveness::threadProc()
@@ -506,7 +548,7 @@ void faceLiveness::initAcionIcon()
     {
         movie = new QMovie(":/images/img-fl-face.png");
         ui->label_fl_actionTips->setText(QString::fromLocal8Bit("请让我看到您的正脸"));
-        ui->btn_fl_time->setVisible(false);
+        //ui->btn_fl_time->setVisible(false);
     }
     else if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_BLINK)
     {
@@ -618,18 +660,19 @@ void faceLiveness::countdownTimer()
 	{
 		--m_nMaxSeconds;
 		ui->label_fl_time->setText(QString("(%1)").arg(m_nMaxSeconds));
-		if (m_nMaxSeconds <= 0)
-		{
-			//整体超时
-			m_bStartCompare = false;
-			m_ActionTimer->stop();
-			m_countdownTimer->stop();
-			{
-				std::scoped_lock sl(m_livenessListMutex);
-				m_livenessList[m_nCurIndex] = m_currentVerifyInfo;
-			}
-			m_sLivenessStatus = STATUS_TYPE::ST_TIME_OUT;
-			saveLivenessResult();
+        if(m_livenessType == FACE_LIVENESS_TYPE::flt_inprogress && m_nMaxSeconds <= 0)
+		{           
+            //整体超时
+            m_bStartCompare = false;
+            m_ActionTimer->stop();
+            m_countdownTimer->stop();
+            {
+                std::scoped_lock sl(m_livenessListMutex);
+                m_livenessList[m_nCurIndex] = m_currentVerifyInfo;
+            }
+            m_sLivenessStatus = STATUS_TYPE::ST_TIME_OUT;
+            saveLivenessResult();
+
 		}
 	}
 	catch (const std::exception &e)
@@ -656,6 +699,7 @@ void faceLiveness::actionTimer()
 			{
 				std::scoped_lock sl(m_livenessListMutex);
 				m_currentVerifyInfo = m_livenessList[m_nCurIndex];
+                m_currentVerifyInfo.reset(m_livenessList[m_nCurIndex].sActionType == ACTION_TYPE ::AT_FACE_DETECT ? m_nFaceDetectDuration : g_appInfoPtr->m_oExamInfo.nActionDuration);
 				initAcionIcon();
 				{
 					std::scoped_lock sl(m_imgMutex);
@@ -678,23 +722,83 @@ void faceLiveness::actionTimer()
 		{
 			if (m_currentVerifyInfo.nActionLeftSceonds <= 0)
 			{
-				//单个超时
-				m_bStartCompare = false;
-				m_currentVerifyInfo.sErrorMsg = QString::fromLocal8Bit("action timeout");
-				m_ActionTimer->stop();
-				m_countdownTimer->stop();
-				{
-					std::scoped_lock sl(m_livenessListMutex);
-					m_livenessList[m_nCurIndex] = m_currentVerifyInfo;
-				}
-				m_sLivenessStatus = STATUS_TYPE::ST_ACTION_FAILED;
-				saveLivenessResult();
-				return;
+                ++m_nFaceRetryCount;
+                {
+                    std::scoped_lock sl(m_imgMutex);
+                    m_imgList.clear();
+                }
+
+                if(m_nFaceRetryCount >= m_nMaxFaceRetryCount)
+                {
+                    if(m_nFaceWholeRetryCount >= m_nMaxFaceWholeRetryCount)
+                    {
+                        //整体失败
+                        m_bStartCompare = false;
+                        m_currentVerifyInfo.sErrorMsg = QString::fromLocal8Bit("action timeout");
+                        m_ActionTimer->stop();
+                        m_countdownTimer->stop();
+                        {
+                            std::scoped_lock sl(m_livenessListMutex);
+                            m_livenessList[m_nCurIndex] = m_currentVerifyInfo;
+                        }
+                        m_sLivenessStatus = STATUS_TYPE::ST_TIME_OUT;
+                        if(m_livenessType == FACE_LIVENESS_TYPE::flt_inprogress)
+                        {
+                            saveLivenessResult();
+                        }
+                        else
+                        {							
+                            showVerifyResultUI();
+							QTimer::singleShot(2000, this, [&]() {
+								emit faceLivenessFaild();
+							});
+                            
+                        }
+                        return;
+                    }
+
+                    ++m_nFaceWholeRetryCount;
+                    {
+                        std::scoped_lock sl(m_imgMutex);
+                        m_imgList.clear();
+                    }
+
+					m_nFaceRetryCount = 0;
+                    m_nCurIndex = 0;
+                    if (m_nCurIndex < m_livenessList.count())
+                    {
+                        std::scoped_lock sl(m_livenessListMutex);
+                        m_currentVerifyInfo = m_livenessList[m_nCurIndex];
+                        m_currentVerifyInfo.reset(m_livenessList[m_nCurIndex].sActionType == ACTION_TYPE::AT_FACE_DETECT ? m_nFaceDetectDuration : g_appInfoPtr->m_oExamInfo.nActionDuration);
+                        initAcionIcon();
+                    }
+                    return;
+                }
+
+                ++m_nFaceRetryCount;
+                m_currentVerifyInfo.reset(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_FACE_DETECT ? m_nFaceDetectDuration : g_appInfoPtr->m_oExamInfo.nActionDuration);
+                initAcionIcon();
 			}
 			else
 			{
 				--m_currentVerifyInfo.nActionLeftSceonds;
 				ui->btn_fl_time->setText(QString("%1s").arg(m_currentVerifyInfo.nActionLeftSceonds));
+
+                if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_FACE_DETECT)
+                {
+                    if(m_currentVerifyInfo.nActionLeftSceonds == 50)
+                    {
+                        ShowMsg(QString::fromLocal8Bit("人脸检测不通过(请调整角度和光线进行尝试,不要戴眼镜)"), (QWidget*)this->parent());
+                    }
+                    else if(m_currentVerifyInfo.nActionLeftSceonds == 30)
+                    {
+                         ShowMsg(QString::fromLocal8Bit("人脸检测不通过(请调整角度和光线进行尝试,不要戴眼镜)"), (QWidget*)this->parent());
+                    }
+                    else if(m_currentVerifyInfo.nActionLeftSceonds == 1)
+                    {
+                         ShowMsg(QString::fromLocal8Bit("若多次尝试不通过,请联系老师更换照片后重试"), (QWidget*)this->parent());
+                    }
+                }
 			}
 		}
 	}
@@ -715,7 +819,16 @@ void faceLiveness::onProcessUpload(CProcessUpload processUpload)
             {
                 m_nRetryCount = 0;
                 m_livenessList[m_nCurIndex].sUrl = processUpload.sFileUrl;
-                saveLivenessResult();
+                if(m_livenessType == FACE_LIVENESS_TYPE::flt_entry_exam &&
+                        m_livenessList[m_nCurIndex].sActionType == ACTION_TYPE::AT_FACE_DETECT)
+                {
+                    saveFaceCampareResult();
+                }
+                else
+                {
+                    saveLivenessResult();
+                }
+
             }
             else
             {
@@ -746,6 +859,82 @@ void faceLiveness::onProcessUpload(CProcessUpload processUpload)
 	}
 }
 
+void faceLiveness::saveFaceCampareResult()
+{
+    CHttpRequestPackage hrp;
+    hrp.sUri = "/api/ecs_oe_student/client/exam/process/saveFaceCompareResult";
+    hrp.nRequestType = RequestType::rtSaveFaceCompareResult;
+    Json::Value jBody = Json::Value::null;
+    jBody["faceCompareResult"] = QString::number(m_livenessList[m_nCurIndex].fSimilarity).toStdString();
+    jBody["fileUrl"] = m_livenessList[m_nCurIndex].sUrl.toStdString();
+    jBody["pass"] = true;
+    jBody["processTime"] = m_livenessList[m_nCurIndex].nEndTime - m_livenessList[m_nCurIndex].nStartTime;
+    jBody["stranger"] = m_livenessList[m_nCurIndex].nFaceCount > 1;
+    hrp.sParamList.push_back(QString("CustomBody,%1").arg(jBody.toStyledString().c_str()));
+    hrp.eParamType = HttpParamType::hptCustomBody;
+
+    g_httpBllPtr->post(hrp);
+}
+
+void faceLiveness::onSaveFaceCompareResult(CBaseResponsePackage res)
+{
+    try
+    {
+        if(res.nCode == 200)
+        {
+            m_nRetryCount = 0;
+            saveLivenessResult();
+        }
+        else
+        {
+            if(m_nRetryCount < 4)
+            {
+                saveFaceCampareResult();
+                m_nRetryCount++;
+            }
+            else
+            {
+                emit faceLivenessFaild();
+            }
+        }
+    }
+    catch (const std::exception &e)
+    {
+        ShowMsg(QString::fromLocal8Bit("保存人脸识别信息失败"), this, MSG_ICON_TYPE::mit_error);
+        myServerLog()<<"exception error"<<e.what();
+    }
+}
+
+void faceLiveness::showVerifyResultUI()
+{
+	if (m_sLivenessStatus == STATUS_TYPE::ST_SUCCESS)
+	{
+		ui->label_fl_hint->setText(QString::fromLocal8Bit("恭喜您完成检测!"));
+		ui->label_fl_icon->setPixmap(QPixmap(":/images/icon-welcom.png"));
+	}
+    else if(m_sLivenessStatus == STATUS_TYPE::ST_TIME_OUT)
+	{
+        ui->label_fl_hint->setText(QString::fromLocal8Bit("检测失败:活体检测超时"));
+		ui->label_fl_icon->setPixmap(QPixmap(":/images/icon-liveness-faild.png"));
+	}
+    else
+    {
+        ui->label_fl_hint->setText(QString::fromLocal8Bit("检测失败:单个动作超时"));
+        ui->label_fl_icon->setPixmap(QPixmap(":/images/icon-liveness-faild.png"));
+    }
+
+	ui->label_fl_hint->adjustSize();
+	ui->widget_fc_BG->setVisible(false);
+	ui->widget_fl_hint->setVisible(true);
+	ui->widget_fl_hint->setGeometry((width() - ui->label_fl_hint->width() - g_appInfoPtr->m_fRate * 180) / 2, (height() - g_appInfoPtr->m_fRate * 180) / 2,
+		ui->label_fl_hint->width() + g_appInfoPtr->m_fRate * 180, g_appInfoPtr->m_fRate * 180);
+	ui->label_fl_icon->setGeometry(g_appInfoPtr->m_fRate * 75, g_appInfoPtr->m_fRate * 70, g_appInfoPtr->m_fRate * 40, g_appInfoPtr->m_fRate * 40);
+
+	ui->label_fl_hint->setGeometry(ui->label_fl_icon->x() + ui->label_fl_icon->width() + g_appInfoPtr->m_fRate * 20,
+		ui->label_fl_icon->y() + (ui->label_fl_icon->height() - ui->label_fl_hint->height()) / 2,
+		ui->label_fl_hint->width(), ui->label_fl_hint->height());
+}
+
 //保存人脸活体验证结果
 void faceLiveness::onSaveFaceLiveVerifyResult(CBaseResponsePackage res)
 {
@@ -769,27 +958,10 @@ void faceLiveness::onSaveFaceLiveVerifyResult(CBaseResponsePackage res)
 				});
 			}
 
+			showVerifyResultUI();
+
 			m_pCloseTimer->start();
-			if (m_sLivenessStatus == STATUS_TYPE::ST_SUCCESS)
-			{
-				ui->label_fl_hint->setText(QString::fromLocal8Bit("恭喜您完成检测!"));
-				ui->label_fl_icon->setPixmap(QPixmap(":/images/icon-welcom.png"));
-			}
-			else
-			{
-				ui->label_fl_hint->setText(QString::fromLocal8Bit("检测失败:单个动作超时"));
-				ui->label_fl_icon->setPixmap(QPixmap(":/images/icon-liveness-faild.png"));
-			}
-			ui->label_fl_hint->adjustSize();
-			ui->widget_fc_BG->setVisible(false);
-			ui->widget_fl_hint->setVisible(true);
-			ui->widget_fl_hint->setGeometry((width() - ui->label_fl_hint->width() - g_appInfoPtr->m_fRate * 180) / 2, (height() - g_appInfoPtr->m_fRate * 180) / 2,
-				ui->label_fl_hint->width() + g_appInfoPtr->m_fRate * 180, g_appInfoPtr->m_fRate * 180);
-			ui->label_fl_icon->setGeometry(g_appInfoPtr->m_fRate * 75, g_appInfoPtr->m_fRate * 70, g_appInfoPtr->m_fRate * 40, g_appInfoPtr->m_fRate * 40);
-
-			ui->label_fl_hint->setGeometry(ui->label_fl_icon->x() + ui->label_fl_icon->width() + g_appInfoPtr->m_fRate * 20,
-				ui->label_fl_icon->y() + (ui->label_fl_icon->height() - ui->label_fl_hint->height()) / 2,
-				ui->label_fl_hint->width(), ui->label_fl_hint->height());
+			
 
 		}
 		else

+ 36 - 1
face/faceLiveness.h

@@ -28,6 +28,12 @@ namespace STATUS_TYPE
     const QString ST_ERROR = "ERROR";
 };
 
+enum class  FACE_LIVENESS_TYPE
+{
+    flt_entry_exam = 1,//考前活体
+    flt_inprogress  //考中活体
+};
+
 struct LivenessVerifyInfo
 {
     bool bIsVerify;//检测完成
@@ -58,6 +64,24 @@ struct LivenessVerifyInfo
         nActionLeftSceonds = 0;
         sErrorMsg = "";
     }
+
+    void reset(int actionLeftSceonds)
+    {
+        bIsVerify = false;
+        sUrl = "";
+        matImage = cv::Mat();
+        bPass = false;
+        nRetryCount = 0;
+        fSimilarity = 0;
+        nRealness = 0;
+        nStartTime = 0;
+        nEndTime = 0;
+        bPass = false;
+        nFaceCount = 0;
+        sErrorMsg = "";
+        nActionLeftSceonds = actionLeftSceonds;
+    }
+
 };
 
 namespace Ui {
@@ -71,7 +95,7 @@ signals:
     void faceLivenessFaild();
     void faceLivenessSucceed();
 public:
-    explicit faceLiveness(QWidget *parent = nullptr);
+    explicit faceLiveness(FACE_LIVENESS_TYPE livenessType, QWidget *parent = nullptr);
     ~faceLiveness();
 
 private slots:
@@ -81,6 +105,7 @@ private slots:
 
     void onProcessUpload(CProcessUpload processUpload); //文件上传
     void onSaveFaceLiveVerifyResult(CBaseResponsePackage res);//保存人脸活体验证结果
+    void onSaveFaceCompareResult(CBaseResponsePackage res);
 
     void actionTimer();
     void countdownTimer();
@@ -91,6 +116,8 @@ private:
     void verifyAction(cv::Mat matImage);
     void onRenderVideoFrame(const char* userId, TRTCVideoStreamType streamType, TRTCVideoFrame* frame);
     bool setBaseImage();
+    void saveFaceCampareResult();
+    void showVerifyResultUI();
 
     Ui::faceLiveness *ui;
 
@@ -109,6 +136,7 @@ private:
 
     std::shared_ptr<QTimer> m_ActionTimer;
     std::shared_ptr<QTimer> m_countdownTimer;
+    std::shared_ptr<QTimer> m_initTimer;
 
     std::mutex m_imgMutex;
     std::list<cv::Mat> m_imgList;
@@ -129,6 +157,13 @@ private:
 
 	bool m_bStartCompare = false;
     int m_nRetryCount = 0;
+    FACE_LIVENESS_TYPE m_livenessType;
+
+	int m_nFaceDetectDuration = 60;
+	int m_nFaceRetryCount = 0;
+    int m_nFaceWholeRetryCount = 0;
+    const int m_nMaxFaceRetryCount = 3;
+    const int m_nMaxFaceWholeRetryCount = 3;
 };
 
 #endif // FACELIVENESS_H

+ 5 - 5
face/faceLiveness.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>673</width>
-    <height>531</height>
+    <width>817</width>
+    <height>677</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -120,10 +120,10 @@
       <widget class="QLabel" name="label_fl_actionTips">
        <property name="geometry">
         <rect>
-         <x>70</x>
+         <x>10</x>
          <y>20</y>
-         <width>54</width>
-         <height>12</height>
+         <width>141</width>
+         <height>20</height>
         </rect>
        </property>
        <property name="text">