You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
845 lines
21 KiB
845 lines
21 KiB
|
|
// Demo_Live_MFCDlg.cpp : 实现文件
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "Demo_Live_MFC.h"
|
|
#include "Demo_Live_MFCDlg.h"
|
|
#include "afxdialogex.h"
|
|
#include "CWFaceDetection.h"
|
|
#include "CWFaceVersion.h"
|
|
#include "CWFaceNisLiveness.h"
|
|
|
|
|
|
#define NIRLIV_FACE_NUM 1 //红外检测人脸数
|
|
#define MAX_DET_NUM_FACES 10 // 检测线程的人脸数
|
|
#define MAX_NISLIVE_NUM_FACES 3 // 红外检测时人脸数
|
|
#define NUM_LIST_MAT 5 // 图片数据链表长度,5个中有三个为活体,则判断为活体
|
|
#define CW_LICENCE "" // 云从授权码
|
|
|
|
#define WM_THREAD_SHOWVIDEO WM_USER + 101 // 自定义消息
|
|
|
|
//线条颜色,画框的
|
|
static cv::Scalar& color = cv::Scalar(0, 0, 255);
|
|
static cv::Scalar& colorGreen = cv::Scalar(0, 255, 0);
|
|
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
|
|
// CDemo_Live_MFCDlg 对话框
|
|
|
|
|
|
|
|
CDemo_Live_MFCDlg::CDemo_Live_MFCDlg(CWnd* pParent /*=NULL*/)
|
|
: CDialogEx(CDemo_Live_MFCDlg::IDD, pParent)
|
|
{
|
|
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
|
|
|
m_pDetThread = nullptr;
|
|
m_bInitNirLiv = false;
|
|
m_bStartThread = false;
|
|
m_pNirLiv = nullptr;
|
|
}
|
|
|
|
void CDemo_Live_MFCDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialogEx::DoDataExchange(pDX);
|
|
DDX_Control(pDX, IDC_EDIT_VERSION, m_edtVersion);
|
|
DDX_Control(pDX, IDC_EDIT_SCORE, m_edtCameraLiveScore);
|
|
DDX_Control(pDX, IDC_EDIT_RESULT, m_edtCameraLiveResult);
|
|
DDX_Control(pDX, IDC_EDIT_TIME, m_edtCameraLiveTime);
|
|
DDX_Control(pDX, IDC_EDIT_SCORE2, m_edtPicLiveScore);
|
|
DDX_Control(pDX, IDC_EDIT_RESULT2, m_edtPicLiveResult);
|
|
DDX_Control(pDX, IDC_EDIT_TIME2, m_edtPicLiveTime);
|
|
DDX_Control(pDX, IDC_STATIC_PIC1, m_staticPic1);
|
|
DDX_Control(pDX, IDC_STATIC_PIC2, m_staticPic2);
|
|
DDX_Control(pDX, IDC_STATIC_VIDEO, m_staticVideo);
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CDemo_Live_MFCDlg, CDialogEx)
|
|
ON_WM_PAINT()
|
|
ON_WM_QUERYDRAGICON()
|
|
ON_BN_CLICKED(IDC_BTN_INIT, &CDemo_Live_MFCDlg::OnBnClickedBtnInit)
|
|
ON_BN_CLICKED(IDC_BTN_OPENUSB, &CDemo_Live_MFCDlg::OnBnClickedBtnOpenusb)
|
|
ON_BN_CLICKED(IDC_BTN_LIVE_CAMERA, &CDemo_Live_MFCDlg::OnBnClickedBtnLiveCamera)
|
|
ON_BN_CLICKED(IDC_BTN_CAMERA, &CDemo_Live_MFCDlg::OnBnClickedBtnCamera)
|
|
ON_BN_CLICKED(IDC_BTN_LOAD1, &CDemo_Live_MFCDlg::OnBnClickedBtnLoad1)
|
|
ON_BN_CLICKED(IDC_BTN_LOAD2, &CDemo_Live_MFCDlg::OnBnClickedBtnLoad2)
|
|
ON_BN_CLICKED(IDC_BTN_PIC_LIVE, &CDemo_Live_MFCDlg::OnBnClickedBtnPicLive)
|
|
ON_MESSAGE(WM_THREAD_SHOWVIDEO, &CDemo_Live_MFCDlg::OnThreadShowVideo)
|
|
ON_WM_DESTROY()
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
// CDemo_Live_MFCDlg 消息处理程序
|
|
|
|
BOOL CDemo_Live_MFCDlg::OnInitDialog()
|
|
{
|
|
CDialogEx::OnInitDialog();
|
|
|
|
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
|
|
// 执行此操作
|
|
SetIcon(m_hIcon, TRUE); // 设置大图标
|
|
SetIcon(m_hIcon, FALSE); // 设置小图标
|
|
|
|
// TODO: 在此添加额外的初始化代码
|
|
m_staticPic1.GetClientRect(&m_rectPic1);
|
|
m_hDCPic1 = m_staticPic1.GetDC()->GetSafeHdc();
|
|
|
|
m_staticPic2.GetClientRect(&m_rectPic2);
|
|
m_hDCPic2 = m_staticPic2.GetDC()->GetSafeHdc();
|
|
|
|
m_staticVideo.GetClientRect(&m_rectVideo);
|
|
m_hDCVideo = m_staticVideo.GetDC()->GetSafeHdc();
|
|
|
|
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
|
|
}
|
|
|
|
// 如果向对话框添加最小化按钮,则需要下面的代码
|
|
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
|
|
// 这将由框架自动完成。
|
|
|
|
void CDemo_Live_MFCDlg::OnPaint()
|
|
{
|
|
if (IsIconic())
|
|
{
|
|
CPaintDC dc(this); // 用于绘制的设备上下文
|
|
|
|
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
|
|
|
|
// 使图标在工作区矩形中居中
|
|
int cxIcon = GetSystemMetrics(SM_CXICON);
|
|
int cyIcon = GetSystemMetrics(SM_CYICON);
|
|
CRect rect;
|
|
GetClientRect(&rect);
|
|
int x = (rect.Width() - cxIcon + 1) / 2;
|
|
int y = (rect.Height() - cyIcon + 1) / 2;
|
|
|
|
// 绘制图标
|
|
dc.DrawIcon(x, y, m_hIcon);
|
|
}
|
|
else
|
|
{
|
|
CDialogEx::OnPaint();
|
|
}
|
|
}
|
|
|
|
//当用户拖动最小化窗口时系统调用此函数取得光标
|
|
//显示。
|
|
HCURSOR CDemo_Live_MFCDlg::OnQueryDragIcon()
|
|
{
|
|
return static_cast<HCURSOR>(m_hIcon);
|
|
}
|
|
|
|
|
|
// 初始化
|
|
void CDemo_Live_MFCDlg::OnBnClickedBtnInit()
|
|
{
|
|
// TODO: 在此添加控件通知处理程序代码
|
|
char szVersion[100] = {0};
|
|
cw_errcode_t errCode = cwGetSDKVersion(szVersion, 100);
|
|
m_edtVersion.SetWindowText((CString)szVersion);
|
|
|
|
// [3/3/2017 Lit]:创建句柄
|
|
if (m_bInitNirLiv)
|
|
{
|
|
MessageBox(_T("已初始化过,不再初始化"));
|
|
return;
|
|
}
|
|
|
|
if (CreateFaceHandle())
|
|
{
|
|
MessageBox(_T("初始化成功"));
|
|
}
|
|
}
|
|
|
|
// 开启双目摄像头
|
|
void CDemo_Live_MFCDlg::OnBnClickedBtnOpenusb()
|
|
{
|
|
// TODO: 在此添加控件通知处理程序代码
|
|
|
|
// TODO: 在此添加控件通知处理程序代码
|
|
if (nullptr == m_pDetThread)
|
|
{
|
|
MessageBox(_T("检测句柄为空,请先初始化创建句柄"));
|
|
return;
|
|
}
|
|
if (m_bStartThread)
|
|
{
|
|
MessageBox(_T("摄像头已打开"));
|
|
return;
|
|
}
|
|
|
|
if (!m_captureVis.open(1))
|
|
{
|
|
MessageBox(_T("可见光摄像头打开失败"));
|
|
return;
|
|
}
|
|
if (!m_captureNis.open(0))
|
|
{
|
|
MessageBox(_T("红外摄像头打开失败"));
|
|
return;
|
|
}
|
|
|
|
m_bStartThread = true;
|
|
m_threadFaceDet = std::thread(&CDemo_Live_MFCDlg::ThreadFaceDet, this);
|
|
m_threadFaceDet.detach();
|
|
}
|
|
|
|
// 动态摄像头红外活体检测
|
|
void CDemo_Live_MFCDlg::OnBnClickedBtnLiveCamera()
|
|
{
|
|
// TODO: 在此添加控件通知处理程序代码
|
|
|
|
if (!m_bStartThread)
|
|
{
|
|
MessageBox(_T("请先打开双目摄像头"));
|
|
return;
|
|
}
|
|
|
|
ListMat visTemp, nisTemp;
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
if (m_listVisFrame.size() != NUM_LIST_MAT)
|
|
{
|
|
MessageBox(_T("可见光图片数据链表长度不为5"));
|
|
return;
|
|
}
|
|
if (m_listNisFrame.size() != NUM_LIST_MAT)
|
|
{
|
|
MessageBox(_T("红外图片数据链表长度不为5"));
|
|
return;
|
|
}
|
|
|
|
visTemp = m_listVisFrame;
|
|
nisTemp = m_listNisFrame;
|
|
}
|
|
|
|
DWORD tmBegin = GetTickCount();
|
|
|
|
ListMat::iterator itVis = visTemp.begin();
|
|
ListMat::iterator itNis = nisTemp.begin();
|
|
cw_nirliv_res_t resLive;
|
|
int iLiveNum = 0;
|
|
for (int i = 0; i < NUM_LIST_MAT; i++, itVis++, itNis++)
|
|
{
|
|
if (GetNisLiveStateByMat(*itVis, *itNis, &resLive) == CW_NIRLIV_OK && resLive.livRst == CW_NIR_LIV_DET_LIVE)
|
|
{
|
|
iLiveNum++;
|
|
}
|
|
}
|
|
|
|
CString sScore, sResult, sTime;
|
|
sTime.Format(_T("%d ms"), GetTickCount() - tmBegin);
|
|
sScore.Format(_T("%d"), iLiveNum);
|
|
if (iLiveNum >= 3)
|
|
{
|
|
sResult = _T("活体");
|
|
}
|
|
else
|
|
{
|
|
sResult = _T("非活体");
|
|
}
|
|
|
|
m_edtCameraLiveScore.SetWindowText(sScore);
|
|
m_edtCameraLiveResult.SetWindowText(sResult);
|
|
m_edtCameraLiveTime.SetWindowText(sTime);
|
|
}
|
|
|
|
// 双目摄像头拍照
|
|
void CDemo_Live_MFCDlg::OnBnClickedBtnCamera()
|
|
{
|
|
// TODO: 在此添加控件通知处理程序代码
|
|
|
|
if (!m_bStartThread)
|
|
{
|
|
MessageBox(_T("请先打开双目摄像头"));
|
|
return;
|
|
}
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
if (!m_listVisFrame.empty())
|
|
{
|
|
cv::imwrite("./1.jpg", m_listVisFrame.back());
|
|
}
|
|
else
|
|
{
|
|
MessageBox(_T("可见光图片数据链表为空"));
|
|
return;
|
|
}
|
|
|
|
if (!m_listNisFrame.empty())
|
|
{
|
|
cv::imwrite("./2.jpg", m_listNisFrame.back());
|
|
}
|
|
else
|
|
{
|
|
MessageBox(_T("红外图片数据链表为空"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_sPic1Path = "./1.jpg";
|
|
m_sPic2Path = "./2.jpg";
|
|
ShowImg(m_sPic1Path, m_hDCPic1, m_rectPic1);
|
|
ShowImg(m_sPic2Path, m_hDCPic2, m_rectPic2);
|
|
}
|
|
|
|
// 导入可见光照片
|
|
void CDemo_Live_MFCDlg::OnBnClickedBtnLoad1()
|
|
{
|
|
// TODO: 在此添加控件通知处理程序代码
|
|
|
|
CFileDialog dlg(TRUE, _T("jpg|bmp|png"), NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
|
|
_T("Image Files(*.jpg;*.bmp;*.png)|*.jpg;*.bmp;*.png|All Files(*.*)|*.*||"), this);
|
|
if (IDOK != dlg.DoModal())
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_sPic1Path = dlg.GetPathName();
|
|
ShowImg(m_sPic1Path, m_hDCPic1, m_rectPic1);
|
|
}
|
|
|
|
// 导入红外照片
|
|
void CDemo_Live_MFCDlg::OnBnClickedBtnLoad2()
|
|
{
|
|
// TODO: 在此添加控件通知处理程序代码
|
|
|
|
CFileDialog dlg(TRUE, _T("jpg|bmp|png"), NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
|
|
_T("Image Files(*.jpg;*.bmp;*.png)|*.jpg;*.bmp;*.png|All Files(*.*)|*.*||"), this);
|
|
if (IDOK != dlg.DoModal())
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_sPic2Path = dlg.GetPathName();
|
|
ShowImg(m_sPic2Path, m_hDCPic2, m_rectPic2);
|
|
}
|
|
|
|
// 静态图片红外活体检测
|
|
void CDemo_Live_MFCDlg::OnBnClickedBtnPicLive()
|
|
{
|
|
// TODO: 在此添加控件通知处理程序代码
|
|
|
|
if (!m_bInitNirLiv)
|
|
{
|
|
MessageBox(_T("请先点击初始化按钮,初始化红外活体检测服务"));
|
|
return;
|
|
}
|
|
|
|
if (m_sPic1Path.IsEmpty())
|
|
{
|
|
MessageBox(_T("请先导入可见光人脸图片"));
|
|
return;
|
|
}
|
|
if (m_sPic2Path.IsEmpty())
|
|
{
|
|
MessageBox(_T("请先导入红外人脸图片"));
|
|
return;
|
|
}
|
|
|
|
cv::Mat matImageVis = cv::imread((std::string)(_bstr_t)(LPCWSTR)m_sPic1Path);
|
|
if (matImageVis.empty())
|
|
{
|
|
MessageBox(_T("PicVis Not Found"));
|
|
return;
|
|
}
|
|
|
|
cv::Mat matImageNis = cv::imread((std::string)(_bstr_t)(LPCWSTR)m_sPic2Path);
|
|
if (matImageNis.empty())
|
|
{
|
|
MessageBox(_T("PicNis Not Found"));
|
|
return;
|
|
}
|
|
|
|
DWORD tmBegin = GetTickCount();
|
|
cw_nirliv_res_t resLive;
|
|
int iRetLive = GetNisLiveStateByMat(matImageVis, matImageNis, &resLive);
|
|
|
|
CString sScore, sResult, sTime;
|
|
sTime.Format(_T("%d ms"), GetTickCount() - tmBegin);
|
|
sScore.Format(_T("%.6f"), resLive.score);
|
|
if (iRetLive == CW_NIRLIV_OK) // 红外活体检测成功,显示状态(活体非活体)
|
|
{
|
|
sResult.Format(_T("%d"), resLive.livRst);
|
|
}
|
|
else // 红外活体检测失败,显示错误码
|
|
{
|
|
sResult.Format(_T("%d"), iRetLive);
|
|
}
|
|
|
|
m_edtPicLiveScore.SetWindowText(sScore);
|
|
m_edtPicLiveResult.SetWindowText(sResult);
|
|
m_edtPicLiveTime.SetWindowText(sTime);
|
|
}
|
|
|
|
int CDemo_Live_MFCDlg::GetNisLiveStateByMat(const cv::Mat &matImageVis, const cv::Mat &matImageNir, cw_nirliv_res_t *res)
|
|
{
|
|
// 判断图片通道值
|
|
cw_img_form_t formatVis;
|
|
if (matImageVis.channels()==1)
|
|
{
|
|
formatVis = CW_IMAGE_GRAY8;
|
|
}
|
|
else if (matImageVis.channels()==3)
|
|
{
|
|
formatVis = CW_IMAGE_BGR888;
|
|
}
|
|
else if (matImageVis.channels()==4)
|
|
{
|
|
formatVis = CW_IMAGE_BGRA8888;
|
|
}
|
|
else
|
|
{
|
|
return CW_NIRLIV_ERR_VISIMAGE;
|
|
}
|
|
|
|
cw_img_form_t formatNir;
|
|
if (matImageNir.channels()==1)
|
|
{
|
|
formatNir = CW_IMAGE_GRAY8;
|
|
}
|
|
else if (matImageNir.channels()==3)
|
|
{
|
|
formatNir = CW_IMAGE_BGR888;
|
|
}
|
|
else if (matImageNir.channels()==4)
|
|
{
|
|
formatNir = CW_IMAGE_BGRA8888;
|
|
}
|
|
else
|
|
{
|
|
return CW_NIRLIV_ERR_NIRIMAGE;
|
|
}
|
|
|
|
cw_nirliv_det_rst_t state = CW_NIR_LIV_DET_IS_INIT;
|
|
float fScore = 0.0f;
|
|
cw_errcode_t errCode = NirLivDet((char*)matImageVis.data, 0, matImageVis.cols, matImageVis.rows, formatVis,
|
|
(char*)matImageNir.data, 0, matImageNir.cols, matImageNir.rows, formatNir, &state, &fScore);
|
|
|
|
res->livRst = state;
|
|
res->score = fScore;
|
|
|
|
return errCode;
|
|
}
|
|
|
|
int CDemo_Live_MFCDlg::GetFaceInfoByMat(void *pDetHandle, const cv::Mat &matImage, cw_face_res_t* pFaceBuffer)
|
|
{
|
|
if (matImage.empty())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
cw_img_t srcImg;
|
|
srcImg.frameId = 0;
|
|
srcImg.data = (char*)matImage.data;
|
|
srcImg.width = matImage.cols;
|
|
srcImg.height = matImage.rows;
|
|
srcImg.angle = CW_IMAGE_ANGLE_0;
|
|
srcImg.mirror = CW_IMAGE_MIRROR_NONE;
|
|
if (matImage.channels()==1)
|
|
{
|
|
srcImg.format = CW_IMAGE_GRAY8;
|
|
}
|
|
else if (matImage.channels()==3)
|
|
{
|
|
srcImg.format = CW_IMAGE_BGR888;
|
|
}
|
|
else if (matImage.channels()==4)
|
|
{
|
|
srcImg.format = CW_IMAGE_BGRA8888;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int iFaceNum = 0;
|
|
// 检测,关键点与质量分
|
|
cw_errcode_t errCode = cwFaceDetection(pDetHandle, &srcImg, pFaceBuffer, MAX_NISLIVE_NUM_FACES, &iFaceNum, CW_OP_DET | CW_OP_KEYPT | CW_OP_QUALITY);
|
|
if (errCode != CW_SDKLIT_OK)
|
|
{
|
|
/*
|
|
CString sErrorTip;
|
|
sErrorTip.Format(_T("人脸检测失败,错误码:%d"), errCode);
|
|
MessageBox(sErrorTip);*/
|
|
return 0;
|
|
}
|
|
if (iFaceNum < 1)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return iFaceNum;
|
|
}
|
|
|
|
void CDemo_Live_MFCDlg::ThreadFaceDet()
|
|
{
|
|
cv::Mat matImage, matImageVis, matImageNis;
|
|
cw_face_res_t faceBuffers[MAX_DET_NUM_FACES];
|
|
while (m_bStartThread)
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
// 可见光
|
|
m_captureVis >> matImageVis;
|
|
if (matImageVis.empty())
|
|
{
|
|
continue;
|
|
}
|
|
// 红外
|
|
m_captureNis >> matImageNis;
|
|
if (matImageNis.empty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (m_listVisFrame.size() >= NUM_LIST_MAT)
|
|
{
|
|
m_listVisFrame.pop_front();
|
|
}
|
|
m_listVisFrame.push_back(matImageVis.clone());
|
|
|
|
if (m_listNisFrame.size() >= NUM_LIST_MAT)
|
|
{
|
|
m_listNisFrame.pop_front();
|
|
}
|
|
m_listNisFrame.push_back(matImageNis.clone());
|
|
|
|
matImage = matImageVis.clone();
|
|
}
|
|
|
|
// 给图像结构体赋值
|
|
cw_img_t srcImg;
|
|
srcImg.frameId = 0;
|
|
srcImg.data = (char*)matImage.data;
|
|
srcImg.width = matImage.cols;
|
|
srcImg.height = matImage.rows;
|
|
srcImg.angle = CW_IMAGE_ANGLE_0;
|
|
srcImg.mirror = CW_IMAGE_MIRROR_NONE;
|
|
if (matImage.channels() ==1 )
|
|
{
|
|
srcImg.format = CW_IMAGE_GRAY8;
|
|
}
|
|
else if (matImage.channels() == 3)
|
|
{
|
|
srcImg.format = CW_IMAGE_BGR888;
|
|
}
|
|
else if (matImage.channels() == 4)
|
|
{
|
|
srcImg.format = CW_IMAGE_BGRA8888;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// 人脸检测
|
|
int iFaceNum = 0;
|
|
cw_errcode_t errCode = cwFaceDetection(m_pDetThread, &srcImg, faceBuffers, MAX_DET_NUM_FACES, &iFaceNum, CW_OP_DET);
|
|
for (int i = 0; i < iFaceNum; i++)
|
|
{
|
|
cv::Point dst1(faceBuffers[i].faceRect.x, faceBuffers[i].faceRect.y);
|
|
cv::Point dst2(faceBuffers[i].faceRect.x + faceBuffers[i].faceRect.width, faceBuffers[i].faceRect.y + faceBuffers[i].faceRect.height);
|
|
//画框
|
|
cv::rectangle(matImage, dst1, dst2, color, 3);
|
|
}
|
|
|
|
SendMessage(WM_THREAD_SHOWVIDEO, 0, (LPARAM)&matImage);
|
|
}
|
|
}
|
|
|
|
LRESULT CDemo_Live_MFCDlg::OnThreadShowVideo(WPARAM wPapam, LPARAM lPapam)
|
|
{
|
|
cv::Mat matImage = *((cv::Mat*)lPapam);
|
|
if (!matImage.empty())
|
|
{
|
|
ShowVideo((char*)(matImage.data), matImage.cols, matImage.rows);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool CDemo_Live_MFCDlg::CreateFaceHandle()
|
|
{
|
|
// 32位和64位检测模型不能共用,64位用_configs_frontend.xml,非64位用_configs_frontend_x86_arm.xml
|
|
#ifdef DEMO_X64
|
|
std::string sModelXmlPath = "../../CWModels/_configs_frontend.xml";
|
|
#else
|
|
std::string sModelXmlPath = "../../CWModels/_configs_frontend_x86_arm.xml";
|
|
#endif // DEMO_X64
|
|
|
|
CString sErrorTip;
|
|
cw_errcode_t errCode = CW_SDKLIT_OK;
|
|
|
|
if (m_pDetThread != nullptr)
|
|
{
|
|
MessageBox(_T("检测句柄已创建过,不再创建"));
|
|
}
|
|
else
|
|
{
|
|
m_pDetThread = cwCreateDetHandle(&errCode, sModelXmlPath.c_str(), CW_LICENCE);
|
|
if (CW_SDKLIT_OK != errCode || nullptr == m_pDetThread)
|
|
{
|
|
sErrorTip.Format(_T("创建检测句柄错误,错误码:%d"), errCode);
|
|
MessageBox(sErrorTip);
|
|
return false;
|
|
}
|
|
|
|
cw_det_param_t param;
|
|
cwGetFaceParam(m_pDetThread, ¶m);
|
|
param.minSize = 48;
|
|
param.maxSize = 600;
|
|
param.pConfigFile = sModelXmlPath.c_str(); // 设置接口功能参数
|
|
cwSetFaceParam(m_pDetThread, ¶m);
|
|
}
|
|
//创建活体句柄
|
|
if (m_pNirLiv == NULL)
|
|
{
|
|
cw_nirliveness_err_t errCodeNir = CW_NIRLIV_OK;
|
|
m_pNirLiv = cwCreateNirLivenessHandle(&errCodeNir,"../../CWModels/nirLiveness_model_20181102_pc.bin","../../CWModels/hd171019.bin",
|
|
"../../CWModels/matrix_para480x640.xml", "./log", 0.35, CW_LICENCE);
|
|
if (NULL == m_pNirLiv)
|
|
{
|
|
sErrorTip.Format(_T("红外活体服务初始化失败,错误码:%d"), errCode);
|
|
MessageBox(sErrorTip);
|
|
return (cw_errcode_t)errCodeNir;
|
|
}
|
|
}
|
|
m_bInitNirLiv = true;
|
|
return true;
|
|
}
|
|
|
|
void CDemo_Live_MFCDlg::ReleaseFaceHandle()
|
|
{
|
|
m_bStartThread = false;
|
|
|
|
m_captureVis.release();
|
|
m_captureNis.release();
|
|
|
|
// 程序退出时销毁句柄
|
|
if (m_pDetThread != nullptr)
|
|
{
|
|
cwReleaseDetHandle(m_pDetThread);
|
|
m_pDetThread = nullptr;
|
|
}
|
|
//关闭红外活体服务
|
|
if (m_pNirLiv != NULL)
|
|
{
|
|
cwReleaseNirLivenessHandle(m_pNirLiv);
|
|
m_pNirLiv = NULL;
|
|
}
|
|
|
|
DeleteDC(m_hDCVideo);
|
|
DeleteDC(m_hDCPic1);
|
|
DeleteDC(m_hDCPic2);
|
|
}
|
|
|
|
|
|
|
|
cw_errcode_t CDemo_Live_MFCDlg::NirLivDet(char* imgDataVis, int iDataLenVis, int iWidthVis, int iHeightVis, cw_img_form_t formatVis,
|
|
char* imgDataNir, int iDataLenNir, int iWidthNir, int iHeightNir, cw_img_form_t formatNir,
|
|
cw_nirliv_det_rst_t *state, float *fScore)
|
|
{
|
|
if (imgDataVis == NULL)
|
|
{
|
|
return (cw_errcode_t)CW_NIRLIV_ERR_VISIMAGE;
|
|
}
|
|
if (imgDataNir == NULL)
|
|
{
|
|
return (cw_errcode_t)CW_NIRLIV_ERR_NIRIMAGE;
|
|
}
|
|
|
|
// 可见光人脸检测
|
|
cw_face_res_t *faceBuffersVis = new cw_face_res_t();
|
|
cw_errcode_t errCode = GetFaceInfoByImg(m_pDetThread, imgDataVis, iDataLenVis, iWidthVis, iHeightVis, formatVis, faceBuffersVis);
|
|
if (errCode != CW_SDKLIT_OK)
|
|
{
|
|
delete faceBuffersVis;
|
|
if (errCode == -1)
|
|
{
|
|
return (cw_errcode_t)CW_NIRLIV_ERR_VIS_NO_FACE;
|
|
}
|
|
else
|
|
{
|
|
return errCode;
|
|
}
|
|
}
|
|
|
|
// 红外人脸检测
|
|
cw_face_res_t *faceBuffersNir = new cw_face_res_t();
|
|
errCode = GetFaceInfoByImg(m_pDetThread, imgDataNir, iDataLenNir, iWidthNir, iHeightNir, formatNir, faceBuffersNir);
|
|
if (errCode != CW_SDKLIT_OK)
|
|
{
|
|
delete faceBuffersVis;
|
|
delete faceBuffersNir;
|
|
if (errCode == -1)
|
|
{
|
|
return (cw_errcode_t)CW_NIRLIV_ERR_NIR_NO_FACE;
|
|
}
|
|
else
|
|
{
|
|
return errCode;
|
|
}
|
|
}
|
|
|
|
// 红外活体检测
|
|
cw_nirliv_detinfo_t detInfo;
|
|
detInfo.nLandmarks = faceBuffersVis->keypt.nkeypt;
|
|
|
|
// 红外图片数据
|
|
detInfo.pNirImg = new cw_nirliv_img_t();
|
|
detInfo.pNirImg->data = imgDataNir;
|
|
detInfo.pNirImg->dataLen = iDataLenNir;
|
|
detInfo.pNirImg->width = iWidthNir;
|
|
detInfo.pNirImg->height = iHeightNir;
|
|
detInfo.pNirImg->format = formatNir;
|
|
|
|
// 可见光图片数据
|
|
detInfo.pVisImg = new cw_nirliv_img_t();
|
|
detInfo.pVisImg->data = imgDataVis;
|
|
detInfo.pVisImg->dataLen = iDataLenVis;
|
|
detInfo.pVisImg->width = iWidthVis;
|
|
detInfo.pVisImg->height = iHeightVis;
|
|
detInfo.pVisImg->format = formatVis;
|
|
|
|
detInfo.pVisInfo = new cw_nirliv_face_param_t(); // 可见光人脸信息
|
|
detInfo.pVisInfo->pKeypoints = faceBuffersVis->keypt.points;
|
|
detInfo.pVisInfo->fKeyScore = faceBuffersVis->keypt.keyptScore; // 质量分
|
|
detInfo.pVisInfo->fSkinScore = faceBuffersVis->quality.scores[6]; // 肤色分
|
|
|
|
detInfo.pNirInfo = new cw_nirliv_face_param_t(); // 红外人脸信息
|
|
detInfo.pNirInfo->pKeypoints = faceBuffersNir->keypt.points;
|
|
detInfo.pNirInfo->fKeyScore = faceBuffersNir->keypt.keyptScore; // 质量分
|
|
detInfo.pNirInfo->fSkinScore = faceBuffersNir->quality.scores[6]; // 肤色分
|
|
|
|
cw_nirliv_res_t *pNisLivenessRes = new cw_nirliv_res_t();
|
|
cw_nirliveness_err_t errNis = cwFaceNirLivenessDet(m_pNirLiv, &detInfo, pNisLivenessRes);
|
|
if (errNis == CW_NIRLIV_OK)
|
|
{
|
|
*state = pNisLivenessRes->livRst;
|
|
*fScore = pNisLivenessRes->score;
|
|
}
|
|
|
|
delete faceBuffersVis;
|
|
delete faceBuffersNir;
|
|
delete detInfo.pNirImg;
|
|
delete detInfo.pVisImg;
|
|
delete detInfo.pVisInfo;
|
|
delete detInfo.pNirInfo;
|
|
delete pNisLivenessRes;
|
|
|
|
return (cw_errcode_t)errNis;
|
|
}
|
|
|
|
cw_errcode_t CDemo_Live_MFCDlg::GetFaceInfoByImg(void *pDetHandle, char *pImgData, int iImgLen, int iWidth,
|
|
int iHeight, cw_img_form_t format, cw_face_res_t* pFaceBuffer)
|
|
{
|
|
cw_img_t srcImg;
|
|
srcImg.frameId = 0;
|
|
srcImg.data = pImgData;
|
|
srcImg.dataLen = iImgLen;
|
|
srcImg.width = iWidth;
|
|
srcImg.height = iHeight;
|
|
srcImg.angle = CW_IMAGE_ANGLE_0;
|
|
srcImg.mirror = CW_IMAGE_MIRROR_NONE;
|
|
srcImg.format = format;
|
|
|
|
int iFaceNum = 0;
|
|
// 检测,关键点与质量分
|
|
cw_errcode_t errCode = cwFaceDetection(pDetHandle, &srcImg, pFaceBuffer, NIRLIV_FACE_NUM, &iFaceNum, CW_OP_DET | CW_OP_KEYPT | CW_OP_QUALITY);
|
|
if (errCode != CW_SDKLIT_OK)
|
|
{
|
|
return errCode;
|
|
}
|
|
if (iFaceNum < 1)
|
|
{
|
|
return (cw_errcode_t)-1;
|
|
}
|
|
|
|
return CW_SDKLIT_OK;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* 显示视频 */
|
|
/************************************************************************/
|
|
void CDemo_Live_MFCDlg::ShowVideo(char* szFrame, int iImgWidth, int iImgHeight)
|
|
{
|
|
BITMAPINFO BmpInfo;
|
|
BmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
BmpInfo.bmiHeader.biWidth = (LONG)iImgWidth;
|
|
BmpInfo.bmiHeader.biHeight = (LONG)iImgHeight;
|
|
|
|
BmpInfo.bmiHeader.biPlanes = 1;
|
|
BmpInfo.bmiHeader.biBitCount = 24;
|
|
BmpInfo.bmiHeader.biCompression = BI_RGB;
|
|
BmpInfo.bmiHeader.biSizeImage = 0;
|
|
BmpInfo.bmiHeader.biXPelsPerMeter = 0;
|
|
BmpInfo.bmiHeader.biYPelsPerMeter = 0;
|
|
BmpInfo.bmiHeader.biClrUsed = 0;
|
|
BmpInfo.bmiHeader.biClrImportant = 0;
|
|
|
|
SetStretchBltMode(m_hDCVideo, COLORONCOLOR);
|
|
StretchDIBits(m_hDCVideo, 0, (m_rectVideo.bottom - m_rectVideo.top), (m_rectVideo.right - m_rectVideo.left), (m_rectVideo.top - m_rectVideo.bottom),
|
|
0, 0, iImgWidth, iImgHeight, szFrame, &BmpInfo, DIB_RGB_COLORS, SRCCOPY);
|
|
}
|
|
|
|
void CDemo_Live_MFCDlg::ShowImg(CString sPicPath, HDC hDCVideo, RECT rectVideo)
|
|
{
|
|
cv::Mat matImage = cv::imread((std::string)(_bstr_t)(LPCWSTR)sPicPath);
|
|
if (matImage.empty())
|
|
{
|
|
return;
|
|
}
|
|
if (matImage.channels() == 4)
|
|
{
|
|
cv::Mat matTemp;
|
|
cv::cvtColor(matImage, matTemp, CV_BGRA2BGR);
|
|
matImage = matTemp.clone();
|
|
}
|
|
|
|
// 要显示宽度必须是4的倍数
|
|
int iWidth = matImage.cols;
|
|
int iHeight = matImage.rows;
|
|
if (0 != iWidth % 4)
|
|
{
|
|
iWidth = iWidth / 4 * 4;
|
|
int iWidthCut = matImage.cols - iWidth;
|
|
int iLeft = iWidthCut / 2;
|
|
int iRight = iWidth + iLeft;
|
|
matImage = matImage.colRange(iLeft, iRight).clone();
|
|
}
|
|
|
|
BITMAPINFO BmpInfo;
|
|
BmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
BmpInfo.bmiHeader.biWidth = (LONG)iWidth;
|
|
BmpInfo.bmiHeader.biHeight = (LONG)iHeight;
|
|
|
|
BmpInfo.bmiHeader.biPlanes = 1;
|
|
BmpInfo.bmiHeader.biBitCount = 24;
|
|
BmpInfo.bmiHeader.biCompression = BI_RGB;
|
|
BmpInfo.bmiHeader.biSizeImage = 0;
|
|
BmpInfo.bmiHeader.biXPelsPerMeter = 0;
|
|
BmpInfo.bmiHeader.biYPelsPerMeter = 0;
|
|
BmpInfo.bmiHeader.biClrUsed = 0;
|
|
BmpInfo.bmiHeader.biClrImportant = 0;
|
|
|
|
SetStretchBltMode(hDCVideo, COLORONCOLOR);
|
|
StretchDIBits(hDCVideo, 0, (rectVideo.bottom - rectVideo.top), (rectVideo.right - rectVideo.left), (rectVideo.top - rectVideo.bottom),
|
|
0, 0, iWidth, iHeight, matImage.data, &BmpInfo, DIB_RGB_COLORS, SRCCOPY);
|
|
}
|
|
|
|
void CDemo_Live_MFCDlg::OnDestroy()
|
|
{
|
|
CDialogEx::OnDestroy();
|
|
|
|
// TODO: 在此处添加消息处理程序代码
|
|
ReleaseFaceHandle();
|
|
}
|
|
|