参考工程:https://github.com/Qengineering/Face-Recognition-Jetson-Nano
人脸识别建库需要调用其中:人脸检测(TRetina.cpp)+人脸对齐(TWarp.cpp)+人脸特征(
TArcface.cpp)。
调试:本次为方便调试,未使用参考工程中cmakelist搭建,选择qt5搭建测试工程,工程qmake.pro 配置:
QT -= gui
CONFIG += c++11 console
CONFIG -= app_bundle
LIBS +=-lpthread \
-fopenmp
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
#add opencv
INCLUDEPATH += /usr/local/include/opencv \
$$PWD/Include/ncnn/
LIBS += /usr/local/lib/libopencv_*
SOURCES += \
src/main_sqlit3.cpp \
Common/cJSON.cpp \
src/TRetina.cpp \
src/TArcface.cpp \
src/TWarp.cpp \
HEADERS += \
Common/cJSON.h \
Common/CppSQLite3.h \
Common/inifile.h \
Common/jsonp.h \
Common/sqlite3.h \
$$PWD/Include/featuredb.h \
$$PWD/Include/TRetina.h \
$$PWD/Include/TArcface.h \
$$PWD/Include/TWarp.h \
#LIBS += -L$$PWD/lib/sqlite -llualibsqlite.0
LIBS += $$PWD/lib/sqlite/libsqlite3.so \
/home/xxx/sqlitefacedb/lib/libncnn.a \
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
db 文件存储人脸模型输出的1x128维度特征;能够支持戴口罩、不带口罩;
db 数据table 类型如:
name | int | NOMASKFEATURE | MASKFEATURE |
XXX_001 | 2 | 1X128 | 1X128 |
XXX_001 | 2 | 1X128 | 1X128 |
基于参考工程调用人脸检测+对其+特征提取:
TWarp Warp; //调用对其
std::vector Faces; //人脸检测数据结构
const int RetinaWidth = 320;
const int RetinaHeight = 240;
std::shared_ptr Rn(new TRetina(RetinaWidth, RetinaHeight, false)); //使用智能指针初始化和调用人脸检测
std::shared_ptr ArcFace(new TArcFace());//使用智能指针初始化和调用人脸特征提取
3.1 创建人脸数据库:
//人脸特征建库前数据存储形式,以下仅参考,对于db文件table 表中的name 和blob 输入可多中方式,如json 也可
std::vector< std::pair > fcdata ; //这里用vector+:pair的形式存储模型输出后names 和人脸的1x128维度的特征;
//如果存储一个人多个人脸可用:
std::vector< std::pair> > mutilfcdata;
以文件夹下img/***1.jpg
img/***2.jpg 为例,遍历文件下所有图片使用检测+对其+特征提取fc,push进vector便于后续导入db 的table(这种方式,后续需要优化,大数据量情况不合理 )。
//创建脸部特征数据库
int getallimgfeature(string &image_dir,std::vector< std::pair > &tests)
{
//获取图片数据
vector NameFaces;
cv::glob(image_dir, NameFaces);
int FaceCnt=NameFaces.size();
if(FaceCnt==0) {
cout << "No image files[jpg] in database" << endl;
return -1;
}
for(int i=0; i tempstring=split(temp, "/");
std::vector tempstring2=split(tempstring[tempstring.size()-1], ".");
Faces.clear();
cv::Mat imgs=cv::imread(temp,1);
cv::Mat result_cnn;
cv::resize(imgs, result_cnn, cv::Size(RetinaWidth,RetinaHeight),cv::INTER_LINEAR);
Rn->detect_retinaface(result_cnn,Faces);
if(Faces[0].FaceProb>0.5){
//get centre aligned image
cv::Mat face_schipss = Warp.Process(result_cnn,Faces[0]);
Faces[0].align_max=face_schipss;
}
else{
cout<<"---std--create fail--:"<GetFeature(Faces[0].align_max); //得到人脸特征,这里仅做测试,没有去求真实的最大人脸
cv::Mat prob;
normalize(prob_fc,prob,1,0,CV_MINMAX); //opencv 接口归一化
string names=tempstring2[0];
cout<
3.2 Sqlite3 db 创建
当我们已获取文件夹下图像的id_names和特征后就可以使用sqlite3相关接口创建人脸数据库了,首先要建立db问价和建立table:
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
sqlite3_stmt* stmt;
//打开数据库文件
rc = sqlite3_open("face_feature.db", &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
fprintf(stdout, "Opened database successfully\n");
}
//创建脸部特征表
sql = "CREATE TABLE FEATURES(" \
"NAME VARCHAR(256) NOT NULL," \
"NUMFEA INT NOT NULL," \
"NOMASKFACE BLOB NOT NULL," \
"MASKFACE BLOB NOT NULL);";
rc = sqlite3_exec(db, sql, NULL, 0, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
fprintf(stdout, "Table created successfully\n");
}
当数据表建立后,我们就需要考虑按照自己table 格式导入我们用 std::vector< std::pair
//导入前的准备语句
sqlite3_prepare(db,"INSERT INTO FEATURES (NAME,NUMFEA,NOMASKFACE,MASKFACE) VALUES(?,?,?,?);",-1,&stmt,NULL); // 导入
//导入已存结构中的id 名字 在tabel 列中
sqlite3_bind_text(stmt,1,entry[i].first.c_str(),-1,SQLITE_STATIC);
//导入已存结构中有几组特征 在tabel 列中 int ,这里只是作为后续扩展一个人脸多个底库特征类型的记录
sqlite3_bind_int(stmt,2,entry[i].second.size[1]);
//导入已存结构中的1x128 cv:Mat 特征 在tabel 列中NOMASKFACE--BLOB中
sqlite3_bind_blob(stmt,3,entry[i].second.data,entry[i].second.size[0]*entry[i].second.size[1]*4,SQLITE_TRANSIENT);
//导入已存结构中的1x128 cv:Mat 特征 在tabel 列中MASKFACE--BLOB中
sqlite3_bind_blob(stmt,4,entry[0].second.data,entry[i].second.size[0]*entry[i].second.size[1]*4,SQLITE_TRANSIENT);
rc = sqlite3_step(stmt);
3.2 Sqlite3 db 生成后调用
当建立face_feature.db后,对于人脸识别系统来说,我们需要遍历db数据table中存储的人脸特征与输入人脸特征做1:N比较,并返回最大pred 得分和对应id name
.... 梳理测中。。。