faceLiveness.cpp 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055
  1. #include "faceLiveness.h"
  2. #include "ui_faceLiveness.h"
  3. #include "CAppInfo.h"
  4. #include <QDesktopWidget>
  5. #include <QFile>
  6. #include <QFileInfo>
  7. #include "awMsgBox.h"
  8. #include "logproc.h"
  9. #include "CCommonTools.h"
  10. #include "CFaceRecProc.h"
  11. faceLiveness::faceLiveness(FACE_LIVENESS_TYPE livenessType, QWidget *parent) :
  12. QWidget(parent),
  13. ui(new Ui::faceLiveness), m_livenessType(livenessType)
  14. {
  15. ui->setupUi(this);
  16. setStyleSheet(g_appInfoPtr->m_sQssStr);
  17. initUI();
  18. qRegisterMetaType<CBaseResponsePackage>("CBaseResponsePackage");
  19. qRegisterMetaType<CClientExamProcessUploadSign>("CClientExamProcessUploadSign");
  20. qRegisterMetaType<CUploadFileToAliyun>("CUploadFileToAliyun");
  21. connect(g_httpBllPtr.get(), &CHttpBll::sgnClientExamProcessUploadSign, this, &faceLiveness::onClientExamProcessUploadSign);
  22. connect(g_httpBllPtr.get(), &CHttpBll::sgnSaveFaceLiveVerifyResult, this, &faceLiveness::onSaveFaceLiveVerifyResult);
  23. connect(g_httpBllPtr.get(), &CHttpBll::sgnSaveFaceCompareResult, this, &faceLiveness::onSaveFaceCompareResult);
  24. connect(g_httpBllPtr.get(), &CHttpBll::sgnUploadFileToAliyun, this, &faceLiveness::onUploadFileToAliyun);
  25. if(m_livenessType == FACE_LIVENESS_TYPE::flt_inprogress)
  26. {
  27. ui->label_fl_time->setVisible(true);
  28. ui->btn_fl_close->setVisible(false);
  29. myServerLog()<< "FACE_LIVENESS_TYPE::flt_inprogress";
  30. }
  31. else
  32. {
  33. ui->label_fl_time->setVisible(false);
  34. ui->btn_fl_close->setVisible(true);
  35. myServerLog()<< "FACE_LIVENESS_TYPE::flt_entry_exam";
  36. }
  37. m_nMaxSeconds = g_appInfoPtr->m_oExamInfo.nAllActionDuration;
  38. QString sFileName = g_appInfoPtr->m_sStudentPhotoPath.right(g_appInfoPtr->m_sStudentPhotoPath.length() - g_appInfoPtr->m_sStudentPhotoPath.lastIndexOf("/") - 1);
  39. sFileName = g_appInfoPtr->m_sCacheFileDir + sFileName;
  40. if(!QFile::exists(sFileName))
  41. {
  42. CHttpRequestPackage hrp;
  43. hrp.sUri = g_appInfoPtr->m_sStudentPhotoPath;
  44. hrp.sCommonStr = sFileName;
  45. hrp.nRequestType = RequestType::rtDownLoadFile;
  46. hrp.nRetryCount = 3;
  47. g_httpBllPtr->downLoad(hrp);
  48. }
  49. m_pVideoTimer = std::make_shared<QTimer>();
  50. m_pVideoTimer->setInterval(200);
  51. connect(m_pVideoTimer.get(), &QTimer::timeout, this, [&](){
  52. cv::Mat frame;
  53. {
  54. std::scoped_lock sl(m_imageMutex);
  55. frame = m_nCurImage;
  56. }
  57. QImage img = CCommonTools::Mat2QImage(frame);
  58. ui->widget_fc_camera->setAutoFillBackground(true);
  59. QPalette palette;
  60. palette.setBrush(QPalette::Window, QBrush(img.scaled(ui->widget_fc_camera->width(), ui->widget_fc_camera->height())));
  61. ui->widget_fc_camera->setPalette(palette);
  62. if(m_bStartCompare && !frame.empty())
  63. {
  64. std::scoped_lock sl(m_imgMutex);
  65. m_imgList.push_back(frame);
  66. }
  67. });
  68. g_clientVideoProcPtr->startTest(this);
  69. m_pVideoTimer->start();
  70. //生成动作列表
  71. QStringList sActionList = g_appInfoPtr->m_oExamInfo.sActionOptions.split(",");
  72. if(sActionList.count() != g_appInfoPtr->m_oExamInfo.nActionNum)
  73. {
  74. ShowMsg(QString::fromLocal8Bit("初始化活体动作失败"), this, MSG_ICON_TYPE::mit_error);
  75. emit faceLivenessFaild();
  76. return;
  77. }
  78. LivenessVerifyInfo lvif;
  79. lvif.sActionType = ACTION_TYPE::AT_FACE_DETECT;
  80. lvif.nActionLeftSceonds = m_nFaceDetectDuration;
  81. m_livenessList.push_back(lvif);
  82. m_currentVerifyInfo = lvif;
  83. if(g_appInfoPtr->m_oExamInfo.sActionOrder == "FIXED")
  84. {
  85. for(QString sAction : sActionList)
  86. {
  87. LivenessVerifyInfo lvi;
  88. lvi.sActionType = sAction;
  89. lvi.nActionLeftSceonds = g_appInfoPtr->m_oExamInfo.nActionDuration;
  90. m_livenessList.push_back(lvi);
  91. }
  92. }
  93. else
  94. {
  95. QList<int> list;
  96. CCommonTools::genRandomNumber(list, g_appInfoPtr->m_oExamInfo.nActionNum);
  97. for(int i = 0; i < g_appInfoPtr->m_oExamInfo.nActionNum; i++)
  98. {
  99. LivenessVerifyInfo lvi;
  100. lvi.sActionType = sActionList[list[i]-1];
  101. lvi.nActionLeftSceonds = g_appInfoPtr->m_oExamInfo.nActionDuration;
  102. m_livenessList.push_back(lvi);
  103. }
  104. }
  105. m_nCurIndex = 0;
  106. m_ActionTimer = std::make_shared<QTimer>();
  107. m_ActionTimer->setInterval(1000);
  108. connect(m_ActionTimer.get(), &QTimer::timeout, this, &faceLiveness::actionTimer);
  109. m_countdownTimer = std::make_shared<QTimer>();
  110. m_countdownTimer->setInterval(1000);
  111. connect(m_countdownTimer.get(), &QTimer::timeout, this, &faceLiveness::countdownTimer);
  112. m_countdownTimer->start();
  113. m_bIsRun = true;
  114. m_thread = std::thread(std::bind(&faceLiveness::threadProc, this));
  115. m_initTimer = std::make_shared<QTimer>();
  116. m_initTimer->setInterval(100);
  117. connect(m_initTimer.get(), &QTimer::timeout, this, [&](){
  118. m_initTimer->stop();
  119. if(g_faceRecProcPtr == nullptr)
  120. {
  121. g_faceRecProcPtr = std::make_shared<CFaceRecProc>();
  122. }
  123. if (!g_appInfoPtr->m_sStudentPhotoPath.isEmpty())
  124. {
  125. // if(!g_faceRecProcPtr->hasBaseImage())
  126. {
  127. if(!setBaseImage())
  128. {
  129. return;
  130. }
  131. }
  132. }
  133. else
  134. {
  135. ShowMsg(QString::fromLocal8Bit("当前考试未底照"), this, MSG_ICON_TYPE::mit_error);
  136. return;
  137. }
  138. // if(!g_faceRecProcPtr->hasBaseImage())
  139. // {
  140. // if(!setBaseImage())
  141. // {
  142. // return;
  143. // }
  144. // }
  145. m_bStartCompare = true;
  146. m_fMaxYaw = 0;
  147. m_fMinYaw = 0;
  148. m_sLivenessStatus = STATUS_TYPE::ST_SUCCESS;
  149. {
  150. std::scoped_lock sl(m_livenessListMutex);
  151. m_currentVerifyInfo = m_livenessList[m_nCurIndex];
  152. }
  153. initAcionIcon();
  154. m_ActionTimer->start();
  155. });
  156. m_initTimer->start();
  157. }
  158. faceLiveness::~faceLiveness()
  159. {
  160. m_pVideoTimer->stop();
  161. m_ActionTimer->stop();
  162. g_clientVideoProcPtr->stopTest();
  163. m_bIsRun = false;
  164. m_thread.join();
  165. awMsgBox::clear(this);
  166. delete ui;
  167. }
  168. void faceLiveness::onRenderVideoFrame(const char* userId, TRTCVideoStreamType streamType, TRTCVideoFrame* frame)
  169. {
  170. if(g_clientVideoProcPtr->isCameraTest())
  171. {
  172. __int64 nServerTime = g_appInfoPtr->serverMTime();
  173. if(nServerTime - m_lastFaceTime >= 100)
  174. {
  175. m_lastFaceTime = nServerTime;
  176. cv::Mat matImg;
  177. cv::cvtColor(cv::Mat(frame->height, frame->width, CV_8UC4, frame->data), matImg, CV_RGBA2RGB);
  178. if(matImg.empty())
  179. {
  180. qDebug()<<"widgetCameraTest frame is empty";
  181. return;
  182. }
  183. std::scoped_lock sl(m_imageMutex);
  184. m_nCurImage = matImg;
  185. }
  186. }
  187. }
  188. void faceLiveness::initUI()
  189. {
  190. QDesktopWidget *dekwiget = QApplication::desktop();
  191. setGeometry(0, 0, dekwiget->width(), dekwiget->height());
  192. ui->widget_mask->setGeometry(0, 0, dekwiget->width(), dekwiget->height());
  193. ui->widget_fc_BG->setGeometry((width() - g_appInfoPtr->m_fRate*800)/2, (height() - g_appInfoPtr->m_fRate*536)/2,
  194. g_appInfoPtr->m_fRate*800, g_appInfoPtr->m_fRate*536);
  195. ui->label_fl_title->adjustSize();
  196. ui->label_fl_title->setGeometry(g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*20,
  197. ui->label_fl_title->width(), ui->label_fl_title->height());
  198. ui->label_fl_time->setGeometry(ui->label_fl_title->x() + ui->label_fl_title->width(), ui->label_fl_title->y(),
  199. g_appInfoPtr->m_fRate*200, ui->label_fl_title->height());
  200. ui->btn_fl_close->setGeometry(ui->widget_fc_BG->width() - g_appInfoPtr->m_fRate*(20 + 100),
  201. g_appInfoPtr->m_fRate*16, g_appInfoPtr->m_fRate*100, g_appInfoPtr->m_fRate*36);
  202. ui->widget_fc_camera->setGeometry(g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*75,
  203. g_appInfoPtr->m_fRate*740, g_appInfoPtr->m_fRate*320);
  204. ui->btn_fl_startVerify->setGeometry((ui->widget_fc_camera->width() - g_appInfoPtr->m_fRate*160)/2,
  205. ui->widget_fc_camera->y() + ui->widget_fc_camera->height() + g_appInfoPtr->m_fRate*30,
  206. g_appInfoPtr->m_fRate*160, g_appInfoPtr->m_fRate*40);
  207. ui->label_fl_tips->adjustSize();
  208. ui->label_fl_tips->setGeometry((ui->widget_fc_BG->width() - ui->label_fl_tips->width())/2, ui->btn_fl_startVerify->y() + ui->btn_fl_startVerify->height() + g_appInfoPtr->m_fRate*20,
  209. ui->label_fl_tips->width(), ui->label_fl_tips ->height());
  210. ui->label_fl_actionMoive->setGeometry(0, 0, g_appInfoPtr->m_fRate*64, g_appInfoPtr->m_fRate*64);
  211. ui->label_fl_actionTips->adjustSize();
  212. int nActionWidth = ui->label_fl_actionMoive->width() + g_appInfoPtr->m_fRate*20 + ui->label_fl_actionTips->width() +
  213. g_appInfoPtr->m_fRate*20 + g_appInfoPtr->m_fRate*44;
  214. ui->widget_fl_action->setGeometry((ui->widget_fc_camera->width() - nActionWidth)/2, g_appInfoPtr->m_fRate*10,
  215. nActionWidth, g_appInfoPtr->m_fRate*64) ;
  216. ui->label_fl_actionTips->setGeometry(ui->label_fl_actionMoive->x() + ui->label_fl_actionMoive->width() + g_appInfoPtr->m_fRate*20,
  217. (ui->widget_fl_action->height() - ui->label_fl_actionTips->height())/2,
  218. ui->label_fl_actionTips->width(), ui->label_fl_actionTips->height());
  219. ui->btn_fl_time->setGeometry(ui->label_fl_actionTips->x() + ui->label_fl_actionTips->width() + g_appInfoPtr->m_fRate*20,
  220. (ui->widget_fl_action->height() - g_appInfoPtr->m_fRate*40)/2, g_appInfoPtr->m_fRate*40, g_appInfoPtr->m_fRate*40);
  221. ui->widget_fl_hint->setVisible(false);
  222. ui->widget_fl_hint->setGeometry((width() - g_appInfoPtr->m_fRate*400)/2, (height() - g_appInfoPtr->m_fRate*180)/2,
  223. g_appInfoPtr->m_fRate*400, g_appInfoPtr->m_fRate*180);
  224. 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);
  225. ui->label_fl_hint->adjustSize();
  226. ui->label_fl_hint->setGeometry(ui->label_fl_icon->x() + ui->label_fl_icon->width() + g_appInfoPtr->m_fRate*20,
  227. ui->label_fl_icon->y() + (ui->label_fl_icon->height() - ui->label_fl_hint->height())/2,
  228. ui->label_fl_hint->width(), ui->label_fl_hint->height());
  229. ui->widget_fl_action->setVisible(false);
  230. ui->btn_fl_startVerify->setVisible(false);
  231. ui->label_fl_tips->setVisible(false);
  232. ui->widget_fc_camera->setFixedHeight(g_appInfoPtr->m_fRate*431);
  233. ui->widget_fl_action->setVisible(true);
  234. }
  235. void faceLiveness::threadProc()
  236. {
  237. try
  238. {
  239. while (m_bIsRun)
  240. {
  241. if (m_bStartCompare)
  242. {
  243. if (m_imgList.begin() != m_imgList.end())
  244. {
  245. myServerLog() << "m_imgList:" << m_imgList.size();
  246. cv::Mat matImage;
  247. {
  248. std::scoped_lock lock(m_imgMutex);
  249. if (m_imgList.begin() != m_imgList.end())
  250. {
  251. matImage = (*m_imgList.begin()).clone();
  252. m_imgList.erase(m_imgList.begin());
  253. }
  254. else
  255. {
  256. continue;
  257. }
  258. }
  259. if (matImage.empty())
  260. {
  261. myServerLog() << "matImage.empty";
  262. continue;
  263. }
  264. if (!m_currentVerifyInfo.bIsVerify)
  265. {
  266. verifyAction(matImage);
  267. }
  268. }
  269. }
  270. else
  271. {
  272. Sleep(100);
  273. }
  274. }
  275. }
  276. catch (const std::exception &e)
  277. {
  278. myDebug()<<QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
  279. }
  280. }
  281. void faceLiveness::verifyAction(cv::Mat matImage)
  282. {
  283. try
  284. {
  285. bool bHasStatus = false;
  286. int nFaceCount = 0;
  287. float fScore = 0;
  288. if (m_currentVerifyInfo.bIsVerify || matImage.empty())
  289. {
  290. return;
  291. }
  292. m_currentVerifyInfo.matImage = matImage;
  293. if (m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_FACE_DETECT)
  294. {
  295. SeetaRect rt;
  296. if (!g_faceRecProcPtr->compareWithBase(matImage, nFaceCount, fScore, rt))
  297. {
  298. m_currentVerifyInfo.sErrorMsg = g_faceRecProcPtr->errorMsg();
  299. myServerLog() << g_faceRecProcPtr->errorMsg();
  300. return;
  301. }
  302. m_currentVerifyInfo.nFaceCount = nFaceCount;
  303. m_currentVerifyInfo.fSimilarity = fScore;
  304. if (nFaceCount != 1)
  305. {
  306. myServerLog() << QString::fromLocal8Bit("活体检测人脸数量异常,") << nFaceCount;
  307. m_currentVerifyInfo.sErrorMsg = QString::fromLocal8Bit("活体检测人脸数量异常,").arg(nFaceCount);
  308. return;
  309. }
  310. if (fScore * 100 > g_appInfoPtr->m_oExamInfo.nFaceThreshold)
  311. {
  312. bool bRealness = false;
  313. if (!g_faceRecProcPtr->faceRealness(matImage, bRealness))
  314. {
  315. myServerLog() << g_faceRecProcPtr->errorMsg();
  316. m_currentVerifyInfo.sErrorMsg = g_faceRecProcPtr->errorMsg();
  317. return;
  318. }
  319. //验证成功
  320. if (bRealness)
  321. {
  322. m_currentVerifyInfo.nEndTime = g_appInfoPtr->serverMTime();
  323. m_currentVerifyInfo.nRealness = 1;
  324. m_currentVerifyInfo.bPass = true;
  325. m_currentVerifyInfo.bIsVerify = true;
  326. myServerLog() << m_currentVerifyInfo.sActionType << ":" << fScore;
  327. return;
  328. }
  329. else
  330. {
  331. m_currentVerifyInfo.nRealness = 0;
  332. }
  333. }
  334. }
  335. if (m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_BLINK ||
  336. m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_NOD)
  337. {
  338. int nFaceStatus = 0;
  339. if (m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_BLINK)
  340. {
  341. nFaceStatus = SL_EYE_CLOSE;
  342. }
  343. else
  344. {
  345. nFaceStatus = SL_HEAD_DOWN;
  346. }
  347. if (!g_faceRecProcPtr->getFaceAttribute(matImage, nFaceStatus, bHasStatus, nFaceCount))
  348. {
  349. myServerLog() << g_faceRecProcPtr->errorMsg();
  350. m_currentVerifyInfo.sErrorMsg = g_faceRecProcPtr->errorMsg();
  351. return;
  352. }
  353. m_currentVerifyInfo.nFaceCount = nFaceCount;
  354. if (nFaceCount != 1)
  355. {
  356. myServerLog() << QString::fromLocal8Bit("活体检测人脸数量异常,") << nFaceCount;
  357. m_currentVerifyInfo.sErrorMsg = QString::fromLocal8Bit("活体检测人脸数量异常,").arg(nFaceCount);
  358. return;
  359. }
  360. if (bHasStatus)
  361. {
  362. //验证成功
  363. m_currentVerifyInfo.nEndTime = g_appInfoPtr->serverMTime();
  364. m_currentVerifyInfo.nRealness = 1;
  365. m_currentVerifyInfo.bPass = true;
  366. m_currentVerifyInfo.bIsVerify = true;
  367. }
  368. }
  369. else if (m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_SHAKE)
  370. {
  371. float fYaw = 0;
  372. float fPitch = 0;
  373. float fRoll = 0;
  374. int nFaceCount = 0;
  375. if (!g_faceRecProcPtr->getFaceAttribute(matImage, fYaw, fPitch, fRoll, nFaceCount))
  376. {
  377. myServerLog() << g_faceRecProcPtr->errorMsg();
  378. m_currentVerifyInfo.sErrorMsg = g_faceRecProcPtr->errorMsg();
  379. return;
  380. }
  381. m_currentVerifyInfo.nFaceCount = nFaceCount;
  382. if (m_fMinYaw > fYaw)
  383. {
  384. m_fMinYaw = fYaw;
  385. }
  386. if (m_fMaxYaw < fYaw)
  387. {
  388. m_fMaxYaw = fYaw;
  389. }
  390. if (m_fMaxYaw - m_fMinYaw > 30)
  391. {
  392. //验证成功
  393. m_currentVerifyInfo.nEndTime = g_appInfoPtr->serverMTime();
  394. m_currentVerifyInfo.nRealness = 1;
  395. m_currentVerifyInfo.bPass = true;
  396. m_currentVerifyInfo.bIsVerify = true;
  397. }
  398. }
  399. }
  400. catch (const std::exception &e)
  401. {
  402. myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
  403. }
  404. }
  405. bool faceLiveness::setBaseImage()
  406. {
  407. QString sFileName = g_appInfoPtr->m_sStudentPhotoPath.right(g_appInfoPtr->m_sStudentPhotoPath.length() - g_appInfoPtr->m_sStudentPhotoPath.lastIndexOf("/") - 1);
  408. sFileName = g_appInfoPtr->m_sCacheFileDir + sFileName;
  409. if(!QFile::exists(sFileName))
  410. {
  411. ShowMsg(QString::fromLocal8Bit("底照下载失败,请检查网络"), this, MSG_ICON_TYPE::mit_error);
  412. return false;
  413. }
  414. QFileInfo filePath(sFileName);
  415. if(!g_faceRecProcPtr->setBaseImage(filePath.absoluteFilePath()))
  416. {
  417. //g_faceRecProcPtr->errorMsg()
  418. ShowMsg(QString::fromLocal8Bit("底照不符合要求,请更换照片"), this, MSG_ICON_TYPE::mit_error);
  419. return false;
  420. }
  421. return true;
  422. }
  423. void faceLiveness::on_btn_fl_startVerify_clicked()
  424. {
  425. if(g_faceRecProcPtr == nullptr)
  426. {
  427. g_faceRecProcPtr = std::make_shared<CFaceRecProc>();
  428. }
  429. if (!g_appInfoPtr->m_sStudentPhotoPath.isEmpty())
  430. {
  431. // if(!g_faceRecProcPtr->hasBaseImage())
  432. {
  433. if(!setBaseImage())
  434. {
  435. return;
  436. }
  437. }
  438. }
  439. else
  440. {
  441. ShowMsg(QString::fromLocal8Bit("当前考试未底照"), this, MSG_ICON_TYPE::mit_error);
  442. return;
  443. }
  444. // if(!g_faceRecProcPtr->hasBaseImage())
  445. // {
  446. // if(!setBaseImage())
  447. // {
  448. // return;
  449. // }
  450. // }
  451. m_bStartCompare = true;
  452. m_fMaxYaw = 0;
  453. m_fMinYaw = 0;
  454. m_sLivenessStatus = STATUS_TYPE::ST_SUCCESS;
  455. ui->btn_fl_startVerify->setVisible(false);
  456. ui->label_fl_tips->setVisible(false);
  457. ui->widget_fc_camera->setFixedHeight(g_appInfoPtr->m_fRate*431);
  458. ui->widget_fl_action->setVisible(true);
  459. {
  460. std::scoped_lock sl(m_livenessListMutex);
  461. m_currentVerifyInfo = m_livenessList[m_nCurIndex];
  462. }
  463. initAcionIcon();
  464. m_ActionTimer->start();
  465. }
  466. void faceLiveness::initAcionIcon()
  467. {
  468. QMovie *movie;
  469. ui->btn_fl_time->setVisible(true);
  470. ui->btn_fl_time->setText(QString("%1s").arg(m_currentVerifyInfo.nActionLeftSceonds));
  471. if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_FACE_DETECT)
  472. {
  473. movie = new QMovie(":/images/img-fl-face.png");
  474. ui->label_fl_actionTips->setText(QString::fromLocal8Bit("请让我看到您的正脸"));
  475. //ui->btn_fl_time->setVisible(false);
  476. }
  477. else if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_BLINK)
  478. {
  479. movie = new QMovie(":/images/gif-close-eyes.gif");
  480. ui->label_fl_actionTips->setText(QString::fromLocal8Bit("请闭眼"));
  481. }
  482. else if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_SHAKE)
  483. {
  484. movie = new QMovie(":/images/gif-turn-head.gif");
  485. ui->label_fl_actionTips->setText(QString::fromLocal8Bit("请摇头"));
  486. }
  487. else if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_NOD)
  488. {
  489. movie = new QMovie(":/images/gif-head-down.gif");
  490. ui->label_fl_actionTips->setText(QString::fromLocal8Bit("请点头"));
  491. }
  492. ui->label_fl_actionMoive->setMovie(movie);
  493. movie->start();
  494. }
  495. void faceLiveness:: saveLivenessResult()
  496. {
  497. try
  498. {
  499. bool bSubmit = true;
  500. for (int i = 0; i < m_livenessList.count(); ++i)
  501. {
  502. if(m_livenessList[i].matImage.empty())
  503. {
  504. myServerLog()<<QString("saveLivenessResult index of %1 matImage is empty").arg(i);
  505. if(m_livenessType == FACE_LIVENESS_TYPE::flt_entry_exam)
  506. {
  507. emit faceLivenessFaild();
  508. return;
  509. }
  510. }
  511. if (!m_livenessList[i].matImage.empty() &&
  512. m_livenessList[i].sUrl.isEmpty())
  513. {
  514. bSubmit = false;
  515. m_nCurIndex = i;
  516. m_currentVerifyInfo = m_livenessList[i];
  517. QString sImageFile = QString("temp/photo/%1.png").arg(CCommonTools::getUuid());
  518. QImage img = CCommonTools::Mat2QImage(m_currentVerifyInfo.matImage);
  519. img.save(sImageFile, "PNG");
  520. //上传图片
  521. CHttpRequestPackage hrp;
  522. hrp.sUri = "/api/ecs_oe_student/client/exam/process/upload/sign";
  523. hrp.nRequestType = RequestType::rtClientExamProcessUploadSign;
  524. hrp.sCommonStr = __FILE__;
  525. hrp.sCommonStr1 = sImageFile;
  526. hrp.sParamList.push_back(QString("fileSuffix,%1").arg(".png"));
  527. hrp.sParamList.push_back(QString("fileMd5,%1").arg(CCommonTools::fileMd5(sImageFile)));
  528. hrp.eParamType = HttpParamType::hptBody;
  529. g_httpBllPtr->post(hrp);
  530. break;
  531. }
  532. }
  533. if (bSubmit)
  534. {
  535. bool bSucceed = true;
  536. Json::Value jLiveness = Json::Value::null;
  537. jLiveness["status"] = m_sLivenessStatus.toStdString();
  538. jLiveness["faceLiveVerifyId"] = g_appInfoPtr->m_oExamInfo.nFaceLiveVerifyId;
  539. jLiveness["processTime"] = m_nEndTime - m_nStartTime;
  540. for (int i = 0; i < m_livenessList.count(); ++i)
  541. {
  542. if (m_livenessList[i].sActionType == ACTION_TYPE::AT_FACE_DETECT)
  543. {
  544. jLiveness["examRecordDataId"] = g_appInfoPtr->m_oExamInfo.nExamRecordDataId;
  545. jLiveness["faceCount"] = m_livenessList[i].nFaceCount;
  546. jLiveness["realness"] = m_livenessList[i].nRealness;
  547. jLiveness["similarity"] = m_livenessList[i].fSimilarity;
  548. jLiveness["fileUrl"] = m_livenessList[i].sUrl.toStdString();
  549. if (m_livenessList[i].nFaceCount == 0)
  550. {
  551. jLiveness["status"] = STATUS_TYPE::ST_NOT_ONESELF.toStdString();
  552. }
  553. }
  554. else
  555. {
  556. Json::Value jAction = Json::Value::null;
  557. if (!m_livenessList[i].bPass)
  558. {
  559. bSucceed = false;
  560. jAction["errorMsg"] = m_livenessList[i].sErrorMsg.toStdString();
  561. }
  562. jAction["fileUrl"] = m_livenessList[i].sUrl.toStdString();
  563. jAction["pass"] = m_livenessList[i].bPass;
  564. jAction["processTime"] = m_livenessList[i].nEndTime - m_livenessList[i].nStartTime;
  565. jAction["retry"] = 0;
  566. jAction["type"] = m_livenessList[i].sActionType.toStdString();
  567. jLiveness["actions"].append(jAction);
  568. }
  569. }
  570. myServerLog() << jLiveness.toStyledString().c_str();
  571. CHttpRequestPackage hrp;
  572. hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/saveFaceLiveVerifyResult");
  573. hrp.nRequestType = RequestType::rtSaveFaceLiveVerifyResult;
  574. hrp.eParamType = HttpParamType::hptCustomBody;
  575. hrp.sParamList.push_back(QString("CustomBody,%1").arg(jLiveness.toStyledString().c_str()));
  576. g_httpBllPtr->post(hrp);
  577. }
  578. }
  579. catch (const std::exception &e)
  580. {
  581. myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
  582. }
  583. }
  584. void faceLiveness::countdownTimer()
  585. {
  586. try
  587. {
  588. --m_nMaxSeconds;
  589. ui->label_fl_time->setText(QString("(%1)").arg(m_nMaxSeconds));
  590. if(m_livenessType == FACE_LIVENESS_TYPE::flt_inprogress && m_nMaxSeconds <= 0)
  591. {
  592. //整体超时
  593. m_bStartCompare = false;
  594. m_ActionTimer->stop();
  595. m_countdownTimer->stop();
  596. {
  597. std::scoped_lock sl(m_livenessListMutex);
  598. m_livenessList[m_nCurIndex] = m_currentVerifyInfo;
  599. }
  600. m_sLivenessStatus = STATUS_TYPE::ST_TIME_OUT;
  601. saveLivenessResult();
  602. }
  603. }
  604. catch (const std::exception &e)
  605. {
  606. myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
  607. }
  608. }
  609. void faceLiveness::actionTimer()
  610. {
  611. try
  612. {
  613. if (m_currentVerifyInfo.bIsVerify && !m_currentVerifyInfo.matImage.empty())
  614. {
  615. //当前比对完成
  616. {
  617. std::scoped_lock sl(m_livenessListMutex);
  618. m_livenessList[m_nCurIndex] = m_currentVerifyInfo;
  619. }
  620. //下一个动作
  621. ++m_nCurIndex;
  622. if (m_nCurIndex < m_livenessList.count())
  623. {
  624. std::scoped_lock sl(m_livenessListMutex);
  625. m_currentVerifyInfo = m_livenessList[m_nCurIndex];
  626. m_currentVerifyInfo.reset(m_livenessList[m_nCurIndex].sActionType == ACTION_TYPE ::AT_FACE_DETECT ? m_nFaceDetectDuration : g_appInfoPtr->m_oExamInfo.nActionDuration);
  627. initAcionIcon();
  628. {
  629. std::scoped_lock sl(m_imgMutex);
  630. m_imgList.clear();
  631. }
  632. }
  633. else
  634. {
  635. //整体比对完成
  636. m_sLivenessStatus = STATUS_TYPE::ST_SUCCESS;
  637. m_bStartCompare = false;
  638. m_ActionTimer->stop();
  639. m_countdownTimer->stop();
  640. saveLivenessResult();
  641. return;
  642. }
  643. }
  644. else
  645. {
  646. if (m_currentVerifyInfo.nActionLeftSceonds <= 0)
  647. {
  648. ++m_nFaceRetryCount;
  649. {
  650. std::scoped_lock sl(m_imgMutex);
  651. m_imgList.clear();
  652. }
  653. if(m_nFaceRetryCount >= m_nMaxFaceRetryCount)
  654. {
  655. if(m_nFaceWholeRetryCount >= m_nMaxFaceWholeRetryCount)
  656. {
  657. //整体失败
  658. m_bStartCompare = false;
  659. m_currentVerifyInfo.sErrorMsg = QString::fromLocal8Bit("action timeout");
  660. m_ActionTimer->stop();
  661. m_countdownTimer->stop();
  662. {
  663. std::scoped_lock sl(m_livenessListMutex);
  664. m_livenessList[m_nCurIndex] = m_currentVerifyInfo;
  665. }
  666. m_sLivenessStatus = STATUS_TYPE::ST_TIME_OUT;
  667. if(m_livenessType == FACE_LIVENESS_TYPE::flt_inprogress)
  668. {
  669. saveLivenessResult();
  670. }
  671. else
  672. {
  673. showVerifyResultUI();
  674. QTimer::singleShot(2000, this, [&]() {
  675. emit faceLivenessFaild();
  676. });
  677. }
  678. return;
  679. }
  680. ++m_nFaceWholeRetryCount;
  681. {
  682. std::scoped_lock sl(m_imgMutex);
  683. m_imgList.clear();
  684. }
  685. m_nFaceRetryCount = 0;
  686. m_nCurIndex = 0;
  687. if (m_nCurIndex < m_livenessList.count())
  688. {
  689. std::scoped_lock sl(m_livenessListMutex);
  690. m_currentVerifyInfo = m_livenessList[m_nCurIndex];
  691. m_currentVerifyInfo.reset(m_livenessList[m_nCurIndex].sActionType == ACTION_TYPE::AT_FACE_DETECT ? m_nFaceDetectDuration : g_appInfoPtr->m_oExamInfo.nActionDuration);
  692. initAcionIcon();
  693. }
  694. return;
  695. }
  696. ++m_nFaceRetryCount;
  697. m_currentVerifyInfo.reset(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_FACE_DETECT ? m_nFaceDetectDuration : g_appInfoPtr->m_oExamInfo.nActionDuration);
  698. initAcionIcon();
  699. }
  700. else
  701. {
  702. --m_currentVerifyInfo.nActionLeftSceonds;
  703. ui->btn_fl_time->setText(QString("%1s").arg(m_currentVerifyInfo.nActionLeftSceonds));
  704. if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_FACE_DETECT)
  705. {
  706. if(m_currentVerifyInfo.nActionLeftSceonds == 50)
  707. {
  708. ShowMsg(QString::fromLocal8Bit("人脸检测不通过(请调整角度和光线进行尝试,不要戴眼镜)"), (QWidget*)this->parent());
  709. }
  710. else if(m_currentVerifyInfo.nActionLeftSceonds == 30)
  711. {
  712. ShowMsg(QString::fromLocal8Bit("人脸检测不通过(请调整角度和光线进行尝试,不要戴眼镜)"), (QWidget*)this->parent());
  713. }
  714. else if(m_currentVerifyInfo.nActionLeftSceonds == 1)
  715. {
  716. ShowMsg(QString::fromLocal8Bit("若多次尝试不通过,请联系老师更换照片后重试"), (QWidget*)this->parent());
  717. }
  718. }
  719. }
  720. }
  721. }
  722. catch (const std::exception &e)
  723. {
  724. myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
  725. }
  726. }
  727. //文件上传
  728. void faceLiveness::onClientExamProcessUploadSign(CClientExamProcessUploadSign processUpload)
  729. {
  730. try
  731. {
  732. if(processUpload.sCommonStr == __FILE__)
  733. {
  734. if(processUpload.nCode == 200)
  735. {
  736. m_nRetryCount = 0;
  737. CHttpRequestPackage hrp;
  738. hrp.sUri = processUpload.sFormUrl;
  739. hrp.nRequestType = RequestType::rtUploadFileToAliyun;
  740. hrp.sCommonStr = __FILE__;
  741. hrp.sCommonStr1 = processUpload.sAccessUrl;
  742. hrp.sParamList.push_back(QString("OSSAccessKeyId,%1").arg(processUpload.sOssAcessKeyId));
  743. hrp.sParamList.push_back(QString("Signature,%1").arg(processUpload.sSignature));
  744. hrp.sParamList.push_back(QString("key,%1").arg(processUpload.sKey));
  745. hrp.sParamList.push_back(QString("policy,%1").arg(processUpload.sPolicy));
  746. hrp.sParamList.push_back(QString("success_action_status,%1").arg(200));
  747. hrp.sParamList.push_back(QString("formdataFileType,file,%1").arg(processUpload.sFilePath));
  748. hrp.bNoHostPrefix = true;
  749. hrp.eParamType = HttpParamType::hptFormdata;
  750. g_httpBllPtr->post(hrp);
  751. }
  752. else
  753. {
  754. if(processUpload.sMessage.isEmpty())
  755. {
  756. ShowMsg(QString::fromLocal8Bit("上传照片失败"), this, MSG_ICON_TYPE::mit_error);
  757. }
  758. else
  759. {
  760. ShowMsg(processUpload.sMessage, this, MSG_ICON_TYPE::mit_error);
  761. }
  762. if(m_nRetryCount < 4)
  763. {
  764. saveLivenessResult();
  765. m_nRetryCount++;
  766. }
  767. else
  768. {
  769. emit faceLivenessFaild();
  770. }
  771. }
  772. }
  773. }
  774. catch (const std::exception &e)
  775. {
  776. ShowMsg(QString::fromLocal8Bit("上传照片失败"), this, MSG_ICON_TYPE::mit_error);
  777. myServerLog()<<"exception error"<<e.what();
  778. }
  779. }
  780. void faceLiveness::onUploadFileToAliyun(CUploadFileToAliyun uploadFileToAliyun)
  781. {
  782. try
  783. {
  784. if(uploadFileToAliyun.sCommonStr == __FILE__)
  785. {
  786. if (uploadFileToAliyun.nCode == 200)
  787. {
  788. m_nRetryCount = 0;
  789. m_livenessList[m_nCurIndex].sUrl = uploadFileToAliyun.sFileUrl;
  790. if(m_livenessType == FACE_LIVENESS_TYPE::flt_entry_exam &&
  791. m_livenessList[m_nCurIndex].sActionType == ACTION_TYPE::AT_FACE_DETECT)
  792. {
  793. saveFaceCampareResult();
  794. }
  795. else
  796. {
  797. saveLivenessResult();
  798. }
  799. }
  800. else
  801. {
  802. if (uploadFileToAliyun.sMessage.isEmpty())
  803. {
  804. ShowMsg(QString::fromLocal8Bit("上传照片失败"), this, MSG_ICON_TYPE::mit_error);
  805. }
  806. else
  807. {
  808. ShowMsg(uploadFileToAliyun.sMessage, this, MSG_ICON_TYPE::mit_error);
  809. }
  810. if(m_nRetryCount < 4)
  811. {
  812. saveLivenessResult();
  813. m_nRetryCount++;
  814. }
  815. else
  816. {
  817. emit faceLivenessFaild();
  818. }
  819. }
  820. }
  821. }
  822. catch (const std::exception &e)
  823. {
  824. myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
  825. }
  826. }
  827. void faceLiveness::saveFaceCampareResult()
  828. {
  829. CHttpRequestPackage hrp;
  830. hrp.sUri = "/api/ecs_oe_student/client/exam/process/saveFaceCompareResult";
  831. hrp.nRequestType = RequestType::rtSaveFaceCompareResult;
  832. Json::Value jBody = Json::Value::null;
  833. jBody["faceCompareResult"] = QString::number(m_livenessList[m_nCurIndex].fSimilarity).toStdString();
  834. jBody["fileUrl"] = m_livenessList[m_nCurIndex].sUrl.toStdString();
  835. jBody["pass"] = true;
  836. jBody["processTime"] = m_livenessList[m_nCurIndex].nEndTime - m_livenessList[m_nCurIndex].nStartTime;
  837. jBody["stranger"] = m_livenessList[m_nCurIndex].nFaceCount > 1;
  838. hrp.sParamList.push_back(QString("CustomBody,%1").arg(jBody.toStyledString().c_str()));
  839. hrp.eParamType = HttpParamType::hptCustomBody;
  840. g_httpBllPtr->post(hrp);
  841. }
  842. void faceLiveness::onSaveFaceCompareResult(CBaseResponsePackage res)
  843. {
  844. try
  845. {
  846. if(res.nCode == 200)
  847. {
  848. m_nRetryCount = 0;
  849. saveLivenessResult();
  850. }
  851. else
  852. {
  853. if(m_nRetryCount < 4)
  854. {
  855. saveFaceCampareResult();
  856. m_nRetryCount++;
  857. }
  858. else
  859. {
  860. emit faceLivenessFaild();
  861. }
  862. }
  863. }
  864. catch (const std::exception &e)
  865. {
  866. ShowMsg(QString::fromLocal8Bit("保存人脸识别信息失败"), this, MSG_ICON_TYPE::mit_error);
  867. myServerLog()<<"exception error"<<e.what();
  868. }
  869. }
  870. void faceLiveness::showVerifyResultUI()
  871. {
  872. if (m_sLivenessStatus == STATUS_TYPE::ST_SUCCESS)
  873. {
  874. ui->label_fl_hint->setText(QString::fromLocal8Bit("恭喜您完成检测!"));
  875. ui->label_fl_icon->setPixmap(QPixmap(":/images/icon-welcom.png"));
  876. }
  877. else if(m_sLivenessStatus == STATUS_TYPE::ST_TIME_OUT)
  878. {
  879. ui->label_fl_hint->setText(QString::fromLocal8Bit("检测失败:活体检测超时"));
  880. ui->label_fl_icon->setPixmap(QPixmap(":/images/icon-liveness-faild.png"));
  881. }
  882. else
  883. {
  884. ui->label_fl_hint->setText(QString::fromLocal8Bit("检测失败:单个动作超时"));
  885. ui->label_fl_icon->setPixmap(QPixmap(":/images/icon-liveness-faild.png"));
  886. }
  887. ui->label_fl_hint->adjustSize();
  888. ui->widget_fc_BG->setVisible(false);
  889. ui->widget_fl_hint->setVisible(true);
  890. ui->widget_fl_hint->setGeometry((width() - ui->label_fl_hint->width() - g_appInfoPtr->m_fRate * 180) / 2, (height() - g_appInfoPtr->m_fRate * 180) / 2,
  891. ui->label_fl_hint->width() + g_appInfoPtr->m_fRate * 180, g_appInfoPtr->m_fRate * 180);
  892. 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);
  893. ui->label_fl_hint->setGeometry(ui->label_fl_icon->x() + ui->label_fl_icon->width() + g_appInfoPtr->m_fRate * 20,
  894. ui->label_fl_icon->y() + (ui->label_fl_icon->height() - ui->label_fl_hint->height()) / 2,
  895. ui->label_fl_hint->width(), ui->label_fl_hint->height());
  896. }
  897. //保存人脸活体验证结果
  898. void faceLiveness::onSaveFaceLiveVerifyResult(CBaseResponsePackage res)
  899. {
  900. try
  901. {
  902. if (res.nCode == 200)
  903. {
  904. m_nRetryCount = 0;
  905. if (m_pCloseTimer == nullptr)
  906. {
  907. m_pCloseTimer = std::make_shared<QTimer>();
  908. m_pCloseTimer->setInterval(2000);
  909. connect(m_pCloseTimer.get(), &QTimer::timeout, this, [&]() {
  910. if (m_sLivenessStatus == STATUS_TYPE::ST_SUCCESS)
  911. {
  912. emit faceLivenessSucceed();
  913. }
  914. else
  915. {
  916. emit faceLivenessFaild();
  917. }
  918. });
  919. }
  920. showVerifyResultUI();
  921. m_pCloseTimer->start();
  922. }
  923. else
  924. {
  925. if (res.sMessage.isEmpty())
  926. {
  927. ShowMsg(QString::fromLocal8Bit("活体验证结果失败"), this, MSG_ICON_TYPE::mit_error);
  928. }
  929. else
  930. {
  931. ShowMsg(res.sMessage, this, MSG_ICON_TYPE::mit_error);
  932. }
  933. if(m_nRetryCount < 4)
  934. {
  935. saveLivenessResult();
  936. m_nRetryCount++;
  937. }
  938. else
  939. {
  940. emit faceLivenessFaild();
  941. }
  942. }
  943. }
  944. catch (const std::exception &e)
  945. {
  946. myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
  947. }
  948. }
  949. void faceLiveness::on_btn_fl_close_clicked()
  950. {
  951. emit faceLivenessFaild();
  952. }