1 本期內(nèi)容介紹
上一期介紹了基于EAIDK的人臉算法應(yīng)用,本期從應(yīng)用角度,解讀一下該案例源碼。
本期案例源碼解讀,主要從源碼目錄結(jié)構(gòu)、配置文件、模型目錄、源碼流程、重點源碼文件等進行解讀。
本期源碼解讀目標:
1. 讓EAIDK-310開發(fā)者對EAIDK-310環(huán)境更加熟練
2. 熟悉EAIDK-310人臉識別案例源碼
3. 方便開發(fā)者使用vision.sdk的調(diào)用流程,為二次開發(fā)做指引參考
2 目錄結(jié)構(gòu)介紹
從ftp://ftp.eaidk.net/EAIDK310/_Source/eaidk310/_face/_package/獲取源碼包后,直接解壓后得到eaidk310_face_package
文件夾,文件夾內(nèi)容如下圖所示:
將eaidk310_face_demo.zip
解壓后,源碼包整體目錄結(jié)構(gòu)如下:
doc目錄:指文檔目錄,包含Vision.Face SDK手冊,Vision.Face SDK人臉算法Q&A手冊。其中Vision.Face SDK手冊,詳細介紹了人臉算法的各個API接口,本案例人臉應(yīng)用調(diào)用的接口全部在該文檔中有介紹。
face-sdk目錄:vision.sdk人臉算法庫(libface.so
)
eaidk310/_face/_demo.zip:eaidk310人臉推廣案例的源代碼包,解壓后的目錄截圖如下:
build-eaidk/_visual/_embedded-Desktop-Debug:編譯目錄和運行目錄,里面有demo運行時必需的文件和目錄,比如配置文件demo.conf,models模型目錄。
eaidk/_visual/_embeded:源碼目錄,案例實現(xiàn)的源代碼都在該目錄下,下文也將重點介紹該目錄下源碼文件。
libs:本案例依賴的vision.sdk人臉庫,內(nèi)容與上文提到的face-sdk目錄內(nèi)容一致。
3 配置文件&模型文件
demo.conf,該文件必須在案例程序的運行的當前路徑,已配置好默認的參數(shù),理解即可。
VideoWidth=1280 -->采集視頻圖像的寬
VideoHeight=720 -->采集視頻圖像的高
Scale=1 -->采集視頻圖像的縮放比例
MinFaceSize=40 -->人臉算法的最低像素要求
Clarity=200 -->人臉算法的最低清晰度要求
FaceAngle=0.4 -->人臉算法的最低角度要求
ThresHold=0.7 -->人臉識別的最低閾值要求
UseApi=0 -->人臉算法的調(diào)用策略
Dbsize=10000 -->人臉庫的最大存儲人臉數(shù)
RegisterMethod=0 -->人臉注冊策略
FacePicturePATH=./models/faces/ -->人臉存儲本地路徑
模型目錄:
mfn.tmfile,mobilefacenet人臉識別模型文件
det1,det2,det3.tmfile,人臉檢測模型文件
face_attr.tmfile,人臉屬性模型文件
4 流程介紹及源碼解讀
4.1 實現(xiàn)流程
整體流程結(jié)構(gòu)如下:
4.2 流程與源碼解讀
下面我們對此案例中的重要&核心的代碼內(nèi)容進行解釋,為讀者提供參考,方便讀者了解vision.sdk算法應(yīng)用。
4.2.1 mainwindow.cpp
程序從main
函數(shù)入口,在main
中創(chuàng)建顯示窗口,具體顯示內(nèi)容在mainwindow.cpp
中實現(xiàn)。mainwindow.cpp
源程序中主要實現(xiàn)2個功能:
1. UI布局
2. 圖像采集
mainwindow.cpp
源程序包含構(gòu)造函數(shù)、updateImage
函數(shù)、open_camera
函數(shù)等重要函數(shù)。
4.2.1.1 構(gòu)造函數(shù)
所有對象創(chuàng)建時,都需要初始化才可以使用,而構(gòu)造函數(shù)就是用于給對象進行初始化,在堆內(nèi)存中開辟出一個空間來存放建立的對象并賦初始值。
/* connects */
connect(&theTimer, &QTimer::timeout, this, &MainWindow::updateImage);
connect(ui->face_attr_2, SIGNAL(clicked(bool)), this, SLOT(convert_to_face_attr()));
connect(ui->face_rec_2, SIGNAL(clicked(bool)), this, SLOT(convert_to_face_rec()));
connect(ui->rb_face_track, SIGNAL(clicked(bool)), this, SLOT(convert_to_face_track()));
updateImage
,是theTimer
對應(yīng)的槽函數(shù),每隔33ms調(diào)用一次,該函數(shù)是mainwindow.cpp
文件中最重要的函數(shù),它包含圖像的顯示、人臉算法處理結(jié)果的顯示。
其他connect
均為顯示界面的各個按鈕及其對應(yīng)槽函數(shù):
/* regist related */
connect(ui->regist, SIGNAL(clicked(bool)), this, SLOT(user_regist()));
connect(ui->cancel, SIGNAL(clicked(bool)), this, SLOT(user_cancel_regist()));
connect(ui->ok, SIGNAL(clicked(bool)), this, SLOT(user_save_regist()));
connect(ui->ok_2, SIGNAL(clicked(bool)), this, SLOT(get_user_name()));
connect(ui->cancel_2, SIGNAL(clicked(bool)), this, SLOT(back_to_face_rec()));
4.2.1.2 updateImage函數(shù)
updateimage
函數(shù)是UI顯示的核心函數(shù),每隔33ms調(diào)用1次,填充視頻窗口的區(qū)域。
void MainWindow::updateImage()
{
ui->label_diku->clear();
ui->label_diku_2->clear();
cam->videoCapL>>cam->srcImageL;
cam->videoCapL>>cam->srcImageL;
該行功能是獲取攝像頭數(shù)據(jù)并存入srcImageL
變量。
updateimage
函數(shù)下調(diào)用了face_rec_enabled
、face_attr_enabled
、face_track_enabled
等函數(shù)
face/_rec/_enabled,表示打開人臉識別功能,該{}內(nèi)表示人臉識別的代碼處理部分。
人臉識別處理部分:
if(face_rec_enabled)
{
algThd->setUseApiParams(0);
algThd->face_rec_label = true;
cv::resize(cam->srcImageL, ResImg, ResImgSiz, CV_INTER_LINEAR);
algThd->sendFrame(ResImg, cam->srcImageL);
Mface face_result = algThd->getFace();
獲取人臉的結(jié)果后,line
函數(shù)對圖像中的人臉畫框,label_diku_2
對人臉圖像做show(顯示)處理。
if(face_result.drawflag && strcmp(face_result.name, "")!=0 && strcmp(face_result.name, "unknown")!=0){
if(access(facepath, F_OK) < 0)
{
printf("%s:%d %s not exist./n", __func__, __LINE__, facepath);
return;
}
int x = face_result.pos[0].x;
int y = face_result.pos[0].y;
int w = face_result.pos[0].width;
int h = face_result.pos[0].height;
line(cam->srcImageL, cvPoint(x, y), cvPoint(x, y + 20), cvScalar(0, 255, 0, 0), 2);
line(cam->srcImageL, cvPoint(x, y), cvPoint(x + 20, y), cvScalar(0, 255, 0, 0), 2);
line(cam->srcImageL, cvPoint(x + w - 20, y), cvPoint(x + w, y), cvScalar(0, 255, 0, 0), 2);
line(cam->srcImageL, cvPoint(x + w, y), cvPoint(x + w, y + 20), cvScalar(0, 255, 0, 0), 2);
line(cam->srcImageL, cvPoint(x, y + h), cvPoint(x + 20, y + h), cvScalar(0, 255, 0, 0), 2);
line(cam->srcImageL, cvPoint(x, y + h), cvPoint(x, y + h - 20), cvScalar(0, 255, 0, 0), 2);
line(cam->srcImageL, cvPoint(x + w, y + h), cvPoint(x + w, y + h - 20), cvScalar(0, 255, 0, 0), 2);
line(cam->srcImageL, cvPoint(x + w, y + h), cvPoint(x + w - 20, y + h), cvScalar(0, 255, 0, 0), 2);
Mat diku = cv::imread(facepath);
cvtColor(diku, diku, CV_BGR2RGB);
QImage imagel = QImage((uchar*)(diku.data), diku.cols, diku.rows, QImage::Format_RGB888);
ui->label_diku->setPixmap(QPixmap::fromImage(imagel));
ui->label_diku->resize(imagel.size());
ui->label_diku->show();
cv::Mat realtime_face;
cam->srcImageL(Rect(face_result.pos[0].x, face_result.pos[0].y, face_result.pos[0].width, face_result.pos[0].height)).copyTo(realtime_face);
cvtColor(realtime_face, realtime_face, CV_BGR2RGB);
cv::resize(realtime_face, realtime_face, Size(120, 120), CV_INTER_LINEAR);
QImage imagel2 = QImage((uchar*)(realtime_face.data), realtime_face.cols, realtime_face.rows, realtime_face.cols*realtime_face.channels(), QImage::Format_RGB888);
ui->label_diku_2->setPixmap(QPixmap::fromImage(imagel2));
ui->label_diku_2->resize(imagel2.size());
ui->label_diku_2->show();
face_attr/enabled,表示打開人臉屬性功能,該{}內(nèi)是人臉屬性演示的代碼部分。
人臉屬性處理部分:
else if(face_attr_enabled)
{
algThd->setUseApiParams(1);
cv::resize(cam->srcImageL, ResImg, ResImgSiz, CV_INTER_LINEAR);
algThd->sendFrame(ResImg, cam->srcImageL);
Mface face_result = algThd->getFace();
face/_track/_enabled,表示打開人臉跟蹤功能,該{}內(nèi)是人臉跟蹤的處理部分。
人臉跟蹤處理部分:
else if(face_track_enabled)
{
algThd->setUseApiParams(6);
cv::resize(cam->srcImageL, ResImg, ResImgSiz, CV_INTER_LINEAR);
algThd->Tracker(ResImg,cam->srcImageL);
cv::resize(cam->srcImageL, ResImg, ResImgSiz, CV_INTER_LINEAR);
algThd->sendFrame(ResImg, cam->srcImageL);
Mface face_result = algThd->getFace();
上面人臉識別、人臉屬性、人臉跟蹤的處理部分都用到了setUseApiParams()
、sendFrame()
、getFace()
函數(shù):
setUseApiParams(),設(shè)置算法處理策略,0表示做人臉識別功能,1表示做人臉屬性功能,6表示做人臉跟蹤功能;
sendFrame(),將采集到的圖像發(fā)送到算法線程進行處理;
getFace(),獲取返回的人臉數(shù)據(jù)。當mainwindow采集圖象時,調(diào)用sendFrame()函數(shù),把采集到的數(shù)據(jù)傳送給循環(huán)處理的線程,并通過getFace()函數(shù)獲取結(jié)果并顯示。
4.2.1.3 open_camera函數(shù)
open_camera
是用來采集USB攝像頭視頻的函數(shù),獲取的圖像用來輸入給人臉算法函數(shù)。其中參數(shù)0表示USB攝像頭的設(shè)備節(jié)點(/dev/video0),當該節(jié)點不存在或者非usb攝像頭設(shè)備時,采集圖像會失敗。
if(cam->videoCapL.open(0))
{
cam->srcImageL = Mat::zeros(cam->videoCapL.get(CV_CAP_PROP_FRAME_HEIGHT), cam->videoCapL.get(CV_CAP_PROP_FRAME_WIDTH), CV_8UC3);
theTimer.start(33);
}
4.2.2 AlgThread.cpp
在mainwindow
類的構(gòu)造函數(shù)中,line131創(chuàng)建人臉算法處理的對象algThd
,并調(diào)用start()
函數(shù)開啟人臉處理線程,其內(nèi)容的實現(xiàn)在AlgThread.cpp
中。人臉算法的流程是輸入圖像、深度學(xué)習(xí)類型人臉算法處理、返回結(jié)果、窗口顯示。
ConfigParam param;
LoadConfig(param,false);
algThd = new AlgThread(param);
algThd->start();
ResImgSiz = cv::Size(NORMAL_FRAME_W, NORMAL_FRAME_H);
AlgThread.cpp
源程序包含線程函數(shù)run
,來實現(xiàn)人臉跟蹤/人臉檢測/特征值提取/人臉特征值比對/人臉注冊等核心功能。
4.2.2.1 線程函數(shù)run
線程函數(shù)run()
是一個while(1)
循環(huán)函數(shù),param.useapi
為0時表示人臉識別,為1時表示人臉屬性,為6時表示人臉跟蹤。
void AlgThread::run()
{
static unsigned long long tv_start, tv_end,t0,t1;
float feature0[FEATURE_SIZE];
float feature1[FEATURE_SIZE];
float score;
for (int i=0;i lck(m_mtx_reg);
getframe(mat);
getSrcframe(grayframe);
if (mat.empty()) continue;
int face_recognize_return_value = FaceRecognize(mat,grayframe,0);;++i)>
其他重要的函數(shù)如下,用黃色背景標注:FaceRecognize
,人臉識別函數(shù)GetFeature
,獲取特征值函數(shù),提取檢測到的人臉的特征值Register
,人臉注冊,特征值存入數(shù)據(jù)庫函數(shù)CompareFaceDB
,人臉比對函數(shù)
int face_recognize_return_value = FaceRecognize(mat,grayframe,0);
if(0x0b == param.useapi)//liveness do not need compare feature
{
continue;
}
printf("%s %d face_recognize_return_value:%d/n", __FUNCTION__, __LINE__, face_recognize_return_value);
if(face_recognize_return_value != SUCCESS){
printf("%s %d face_recognize fail.../n", __FUNCTION__, __LINE__);
t0=tv_start;
if (param.useapi==2) {
//gettimeofday(&tv_end, NULL);
//tv_end = get_ms();
//m_fps = (float)1000.0 /(tv_end.tv_sec * 1000 + tv_end.tv_usec / 1000 - tv_start.tv_sec * 1000 - tv_start.tv_usec / 1000);
}
continue;
}
else
{
printf("%s %d face_recognize success.../n", __FUNCTION__, __LINE__);
}
if(1 == param.useapi)
{
continue;
}
ret=faceapp::GetFeature(mFace,feature1,&res);
if (ret!=SUCCESS) {
printf("%s %d get feature fail..../n", __FUNCTION__, __LINE__);
t0=tv_start;
if(ret==ERROR_BAD_QUALITY)
m_face.nam;
continue;
}
if(regist_label){
Register(mat, feature1, (char*)reg_name.c_str());
regist_label = false;
reg_name = "";
}
if(face_rec_label){
ret = CompareFaceDB(feature1);
vision.sdk的使用,流程是初始化->人臉檢測->人臉特征值提取->人臉注冊->人臉比對函數(shù)使用,如上函數(shù)調(diào)用順序基本也是vision.sdk的調(diào)用順序:
5 總結(jié)
該案例解讀文檔主要主要從源碼目錄結(jié)構(gòu)、源碼簡要流程、重點源碼函數(shù)幾個方面進行了解讀和介紹,并按照功能實現(xiàn)流程順序,介紹了基于EAIDK的人臉識別案例源碼中的重要函數(shù)及其實現(xiàn)的功能,并貼出實際使用代碼,方便開發(fā)者理解,為開發(fā)者進行二次開發(fā)提供參考。
審核編輯 黃昊宇
-
算法
+關(guān)注
關(guān)注
23文章
4607瀏覽量
92828 -
人臉識別
+關(guān)注
關(guān)注
76文章
4011瀏覽量
81859 -
深度學(xué)習(xí)
+關(guān)注
關(guān)注
73文章
5500瀏覽量
121111
發(fā)布評論請先 登錄
相關(guān)推薦
評論