安基网 首页 编程 软件学院 查看内容

Qt编写百度离线版人脸识别+比对+活体检测

2018-9-17 08:05| 投稿: xiaotiger |来自: 互联网


免责声明:本站系公益性非盈利IT技术普及网,本文由投稿者转载自互联网的公开文章,文末均已注明出处,其内容和图片版权归原网站或作者所有,文中所述不代表本站观点,若有无意侵权或转载不当之处请从网站右下角联系我们处理,谢谢合作!

摘要: 在AI技术发展迅猛的今天,很多设备都希望加上人脸识别功能,好像不加上点人脸识别功能感觉不够高大上,都往人脸识别这边靠,手机刷脸解锁,刷脸支付,刷脸开门,刷脸金融,刷脸安防,是不是以后还可以刷脸匹配男女交 ...

在AI技术发展迅猛的今天,很多设备都希望加上人脸识别功能,好像不加上点人脸识别功能感觉不够高大上,都往人脸识别这边靠,手机刷脸解锁,刷脸支付,刷脸开门,刷脸金融,刷脸安防,是不是以后还可以刷脸匹配男女交友?

很多人认为人脸识别直接用opencv做,其实那只是极其基础的识别个人脸,然并卵,好比学C++写了个hello类似。拿到人脸区域图片只是万里长征的第一步,真正能够起作用的是人脸特征值的提取,然后用于搜索和查找人脸,比如两张图片比较相似度,从一堆人脸库中找到最相似的人脸,对当前人脸识别是否是活体等。

对于可以接入外网的设备,可以直接通过在线api的http请求方式获得结果,但是有很多应用场景是离线的,或者说不通外网,只能局域网使用,为了安全性考虑,这个时候就要求所有的人脸处理在本地完成,本篇文章采用的百度离线SDK作为解决方案。可以去官网申请,默认有6个免费的密钥使用三个月,需要与本地设备的指纹信息匹配,感兴趣的同学可以自行去官网下载SDK。

百度离线人脸识别SDK文件比较大,光模型文件就645MB,估计这也许是识别率比较高的一方面原因吧,不断训练得出的模型库,本篇文章只放出Qt封装部分源码。官网对应的使用说明还是非常详细的,只要是学过编程的人就可以看懂。

第一步:初始化SDK

第二步:执行动作,比如查找人脸、图片比对、特征值比对等

头文件代码:

#ifndef FACEBAIDULOCAL_H
#define FACEBAIDULOCAL_H
/**
 * 百度离线版人脸识别+人脸比对等功能类 作者:feiyangqingyun(QQ:517216493) 2018-8-30
 * 1:支持活体检测
 * 2:可设置最大队列中的图片数量
 * 3:多线程处理,通过type控制当前处理类型
 * 4:支持单张图片检索相似度最高的图片
 * 5:支持指定目录图片生成特征文件
 * 6:支持两张图片比对方式
 * 7:可设置是否快速查找
 * 8:可设置是否统计用时
 */
#include 
#include 
#if (QT_VERSION > QT_VERSION_CHECK(5,0,0))
#include 
#endif
#include "baidu_face_api.h"
class FaceBaiDuLocal : public QThread
{
 Q_OBJECT
public:
 static FaceBaiDuLocal *Instance();
 explicit FaceBaiDuLocal(QObject *parent = 0);
 ~FaceBaiDuLocal();
protected:
 void run();
private:
 static QScopedPointer self;
 BaiduFaceApi *api;
 std::vector *faces;
 QMutex mutex; //锁对象
 bool stopped; //线程停止标志位
 int maxCount; //最大图片张数
 int type; //当前处理类型
 int percent; //最小人脸百分比
 int delayms; //减去毫秒数,用于造假
 bool findFast; //是否快速模式
 bool countTime; //统计用时
 bool busy; //是否正忙
 QList flags; //等待处理的图像队列的名称
 QList imgs; //等待处理的图像队列
 QList imgs2; //等待处理的比对图像队列
 QString sdkPath; //SDK目录
 QString imgDir; //图片目录
 QImage oneImg; //单张图片比对找出最大特征图像
 QList imgNames; //图像队列
 QList > features; //特征队列
signals:
 //人脸区域坐标返回
 void receiveFaceRect(const QString &flag, const QRect &rect, int msec);
 //获取人脸区域坐标失败
 void receiveFaceRectFail(const QString &flag);
 //人脸特征返回
 void receiveFaceFeature(const QString &flag, const QList &feature, int msec);
 //获取人脸特征失败
 void receiveFaceFeatureFail(const QString &flag);
 //人脸比对结果返回
 void receiveFaceCompare(const QString &flag, float result, int msec);
 //人脸比对失败
 void receiveFaceCompareFail(const QString &flag);
 //单张图片检索最大相似度结果返回
 void receiveFaceCompareOne(const QString &flag, const QImage &srcImg, const QString &targetName, float result);
 //所有人脸特征提取完毕
 void receiveFaceFeatureFinsh();
 //活体检测返回
 void receiveFaceLive(const QString &flag, float result, int msec);
 //活体检测失败
 void receiveFaceLiveFail(const QString &flag);
public slots:
 //初始化SDK
 void init();
 //停止处理线程
 void stop();
 //获取当前是否忙
 bool getBusy();
 //设置图片队列最大张数
 void setMaxCount(int maxCount);
 //设置当前处理类型
 void setType(int type);
 //设置最小人脸百分比
 void setPercent(int percent);
 //设置减去毫秒数
 void setDelayms(int delayms);
 //设置是否快速模式
 void setFindFast(bool findFast);
 //设置是否统计用时
 void setCountTime(bool countTime);
 //设置是否忙
 void setBusy(bool busy);
 //设置SDK目录
 void setSDKPath(const QString &sdkPath);
 //设置要将图片提取出特征的目录
 void setImgDir(const QString &imgDir);
 //设置单张需要检索的图片
 void setOneImg(const QString &flag, const QImage &oneImg);
 //往队列中追加单张图片等待处理
 void append(const QString &flag, const QImage &img);
 //往队列中追加两张图片等待比对
 void append(const QString &flag, const QImage &img, const QImage &img2);
 //自动加载目录下的所有图片的特征
 void getFaceFeatures(const QString &imgDir);
 //获取人脸区域
 bool getFaceRect(const QString &flag, const QImage &img, QRect &rect, int &msec);
 //活体检测
 bool getFaceLive(const QString &flag, const QImage &img, float &result, int &msec);
 //获取人脸特征
 bool getFaceFeature(const QString &flag, const QImage &img, QList &feature, int &msec);
 //人脸比对,传入两张照片特征
 float getFaceCompare(const QString &flag, const QList &feature1, const QList &feature2);
 //人脸比对,传入两张照片
 bool getFaceCompare(const QString &flag, const QImage &img1, const QImage &img2, float &result, int &msec);
 //从一堆图片中找到最像的一张图片
 void getFaceOne(const QString &flag, const QImage &img, QString &targetName, float &result);
 //指定特征找到照片
 void getFaceOne(const QString &flag, const QList &feature, QString &targetName, float &result);
 //添加人脸
 void appendFace(const QString &flag, const QImage &img, const QString &txtFile);
 //删除人脸
 void deleteFace(const QString &flag);
};
#endif // FACEBAIDULOCAL_H

实现文件完整代码:

#include "facebaidulocal.h"
#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz"))
QByteArray getImageData(const QImage &image)
{
 QByteArray imageData;
 QBuffer buffer(&imageData);
 image.save(&buffer, "JPG");
 imageData = imageData.toBase64();
 return imageData;
}
QScopedPointer FaceBaiDuLocal::self;
FaceBaiDuLocal *FaceBaiDuLocal::Instance()
{
 if (self.isNull()) {
 QMutex mutex;
 QMutexLocker locker(&mutex);
 if (self.isNull()) {
 self.reset(new FaceBaiDuLocal);
 }
 }
 return self.data();
}
FaceBaiDuLocal::FaceBaiDuLocal(QObject *parent) : QThread(parent)
{
 //注册信号中未知的数据类型
 qRegisterMetaType >("QList");
 stopped = false;
 maxCount = 100;
 type = 1;
 percent = 8;
 delayms = 0;
 findFast = false;
 countTime = true;
 busy = false;
 sdkPath = qApp->applicationDirPath() + "/facesdk";
 imgDir = "";
 oneImg = QImage();
 api = new BaiduFaceApi;
 faces = new std::vector();
}
FaceBaiDuLocal::~FaceBaiDuLocal()
{
 delete api;
 this->stop();
 this->wait(1000);
}
void FaceBaiDuLocal::run()
{
 this->init();
 while(!stopped) {
 int count = flags.count();
 if (count > 0) {
 QMutexLocker lock(&mutex);
 busy = true;
 if (type == 0) {
 QString flag = flags.takeFirst();
 QImage img = imgs.takeFirst();
 QRect rect;
 int msec;
 if (getFaceRect(flag, img, rect, msec)) {
 emit receiveFaceRect(flag, rect, msec);
 } else {
 emit receiveFaceRectFail(flag);
 }
 } else if (type == 1) {
 QString flag = flags.takeFirst();
 QImage img = imgs.takeFirst();
 QList feature;
 int msec;
 if (getFaceFeature(flag, img, feature, msec)) {
 emit receiveFaceFeature(flag, feature, msec);
 } else {
 emit receiveFaceFeatureFail(flag);
 }
 } else if (type == 2) {
 QString flag = flags.takeFirst();
 QImage img1 = imgs.takeFirst();
 QImage img2 = imgs2.takeFirst();
 float result;
 int msec;
 if (getFaceCompare(flag, img1, img2, result, msec)) {
 emit receiveFaceCompare(flag, result, msec);
 } else {
 emit receiveFaceCompareFail(flag);
 }
 } else if (type == 3) {
 flags.takeFirst();
 getFaceFeatures(imgDir);
 } else if (type == 4) {
 QString flag = flags.takeFirst();
 QString targetName;
 float result;
 getFaceOne(flag, oneImg, targetName, result);
 if (!targetName.isEmpty()) {
 emit receiveFaceCompareOne(flag, oneImg, targetName, result);
 }
 } else if (type == 5) {
 QString flag = flags.takeFirst();
 QImage img = imgs.takeFirst();
 float result;
 int msec;
 if (getFaceLive(flag, img, result, msec)) {
 emit receiveFaceLive(flag, result, msec);
 } else {
 emit receiveFaceLiveFail(flag);
 }
 }
 }
 msleep(100);
 busy = false;
 }
 stopped = false;
}
void FaceBaiDuLocal::init()
{
 int res = api->sdk_init();
 res = api->is_auth();
 if(res != 1) {
 qDebug() << TIMEMS << QString("init sdk error: %1").arg(res);
 return;
 } else {
 //设置最小人脸,默认30
 api->set_min_face_size(percent);
 //设置光照阈值,默认40
 api->set_illum_thr(20);
 //设置角度阈值,默认15
 //api->set_eulur_angle_thr(30, 30, 30);
 qDebug() << TIMEMS << "init sdk ok";
 }
}
void FaceBaiDuLocal::stop()
{
 stopped = true;
}
bool FaceBaiDuLocal::getBusy()
{
 return this->busy;
}
void FaceBaiDuLocal::setMaxCount(int maxCount)
{
 if (maxCount <= 1000) {
 this->maxCount = maxCount;
 }
}
void FaceBaiDuLocal::setType(int type)
{
 if (this->type != type) {
 this->type = type;
 this->flags.clear();
 this->imgs.clear();
 this->imgs2.clear();
 }
}
void FaceBaiDuLocal::setPercent(int percent)
{
 this->percent = percent;
}
void FaceBaiDuLocal::setDelayms(int delayms)
{
 this->delayms = delayms;
}
void FaceBaiDuLocal::setFindFast(bool findFast)
{
 this->findFast = findFast;
}
void FaceBaiDuLocal::setCountTime(bool countTime)
{
 this->countTime = countTime;
}
void FaceBaiDuLocal::setBusy(bool busy)
{
 this->busy = busy;
}
void FaceBaiDuLocal::setSDKPath(const QString &sdkPath)
{
 this->sdkPath = sdkPath;
}
void FaceBaiDuLocal::setImgDir(const QString &imgDir)
{
 this->imgDir = imgDir;
 this->flags.clear();
 this->flags.append("imgDir");
 this->type = 3;
}
void FaceBaiDuLocal::setOneImg(const QString &flag, const QImage &oneImg)
{
 setType(4);
 //需要将图片重新拷贝一个,否则当原图像改变之后也会改变
 this->oneImg = oneImg.copy();
 this->flags.append(flag);
}
void FaceBaiDuLocal::append(const QString &flag, const QImage &img)
{
 QMutexLocker lock(&mutex);
 int count = flags.count();
 if (count < maxCount) {
 flags.append(flag);
 imgs.append(img);
 }
}
void FaceBaiDuLocal::append(const QString &flag, const QImage &img, const QImage &img2)
{
 QMutexLocker lock(&mutex);
 int count = flags.count();
 if (count < maxCount) {
 flags.append(flag);
 imgs.append(img);
 imgs2.append(img2);
 }
}
void FaceBaiDuLocal::getFaceFeatures(const QString &imgDir)
{
 imgNames.clear();
 features.clear();
 //载入指定目录图像处理特征
 QDir imagePath(imgDir);
 QStringList filter;
 filter << "*.jpg" << "*.bmp" << "*.png" << "*.jpeg" << "*.gif";
 imgNames.append(imagePath.entryList(filter));
 qDebug() << TIMEMS << "getFaceFeatures" << imgNames;
 //从目录下读取同名的txt文件(存储的特征)
 //如果存在则从文件读取特征,如果不存在则转码解析出特征
 //转码完成后将得到的特征存储到同名txt文件
 int count = imgNames.count();
 for (int i = 0; i < count; i++) {
 QList feature;
 int msec;
 QString imgName = imgNames.at(i);
 QStringList list = imgName.split(".");
 QString txtName = imgDir + "/" + list.at(0) + ".txt";
 QFile file(txtName);
 if (file.exists()) {
 if (file.open(QFile::ReadOnly)) {
 QString data = file.readAll();
 file.close();
 qDebug() << TIMEMS << "readFaceFeature" << txtName;
 QStringList list = data.split(",");
 foreach (QString str, list) {
 if (!str.isEmpty()) {
 feature.append(str.toFloat());
 }
 }
 }
 } else {
 QImage img(imgDir + "/" + imgName);
 bool ok = getFaceFeature(imgName, img, feature, msec);
 if (ok) {
 emit receiveFaceFeature(imgName, feature, msec);
 if (file.open(QFile::WriteOnly)) {
 QStringList list;
 foreach (float fea, feature) {
 list.append(QString::number(fea));
 }
 qDebug() << TIMEMS << "writeFaceFeature" << txtName;
 file.write(list.join(",").toLatin1());
 file.close();
 }
 }
 }
 features.append(feature);
 msleep(1);
 }
 qDebug() << TIMEMS << "getFaceFeatures finsh";
 emit receiveFaceFeatureFinsh();
}
bool FaceBaiDuLocal::getFaceRect(const QString &flag, const QImage &img, QRect &rect, int &msec)
{
 //qDebug() << TIMEMS << flag << "getFaceRect";
 QTime time;
 if (countTime) {
 time.start();
 }
 faces->clear();
 QByteArray imageData = getImageData(img);
 int result = api->track_max_face(faces, imageData.constData(), 1);
 if (result == 1) {
 TrackFaceInfo info = faces->at(0);
 FaceInfo ibox = info.box;
 float width = ibox.mWidth;
 float x = ibox.mCenter_x;
 float y = ibox.mCenter_y;
 rect = QRect(x - width / 2, y - width / 2, width, width);
 if (countTime) {
 msec = time.elapsed() - delayms;
 } else {
 msec = delayms;
 }
 msec = msec < 0 ? 0 : msec;
 return true;
 } else {
 return false;
 }
 return false;
}
bool FaceBaiDuLocal::getFaceLive(const QString &flag, const QImage &img, float &result, int &msec)
{
 //qDebug() << TIMEMS << flag << "getFaceLive";
 QTime time;
 if (countTime) {
 time.start();
 }
 result = 0;
 QByteArray imageData = getImageData(img);
 std::string value = api->rgb_liveness_check(imageData.constData(), 1);
 QString data = value.c_str();
 data = data.replace("\t", "");
 data = data.replace("\"", "");
 data = data.replace(" ", "");
 int index = -1;
 QStringList list = data.split("\n");
 foreach (QString str, list) {
 index = str.indexOf("score:");
 if (index >= 0) {
 result = str.mid(6, 4).toFloat();
 break;
 }
 }
 if (index >= 0) {
 if (countTime) {
 msec = time.elapsed() - delayms;
 } else {
 msec = delayms;
 }
 msec = msec < 0 ? 0 : msec;
 return true;
 } else {
 return false;
 }
 return false;
}
bool FaceBaiDuLocal::getFaceFeature(const QString &flag, const QImage &img, QList &feature, int &msec)
{
 //qDebug() << TIMEMS << flag << "getFaceFeature" << img.width() << img.height() << img.size();
 QTime time;
 if (countTime) {
 time.start();
 }
 const float *fea = nullptr;
 QByteArray imageData = getImageData(img);
 int result = api->get_face_feature(imageData.constData(), 1, fea);
 if (result == 512) {
 feature.clear();
 for (int i = 0; i < 512; i++) {
 feature.append(fea[i]);
 }
 if (countTime) {
 msec = time.elapsed() - delayms;
 } else {
 msec = delayms;
 }
 msec = msec < 0 ? 0 : msec;
 return true;
 } else {
 return false;
 }
 return false;
}
float FaceBaiDuLocal::getFaceCompare(const QString &flag, const QList &feature1, const QList &feature2)
{
 //qDebug() << TIMEMS << flag << "getFaceCompareXXX";
 std::vector fea1, fea2;
 for (int i = 0; i < 512; i++) {
 fea1.push_back(feature1.at(i));
 fea2.push_back(feature2.at(i));
 }
 float result = api->compare_feature(fea1, fea2);
 //过滤非法的值
 result = result > 100 ? 0 : result;
 return result;
}
bool FaceBaiDuLocal::getFaceCompare(const QString &flag, const QImage &img1, const QImage &img2, float &result, int &msec)
{
 //qDebug() << TIMEMS << flag << "getFaceCompare";
 result = 0;
 bool ok1, ok2;
 QList feature1, feature2;
 int msec1, msec2;
 QString flag1, flag2;
 if (flag.contains("|")) {
 QStringList list = flag.split("|");
 flag1 = list.at(0);
 flag2 = list.at(1);
 } else {
 flag1 = flag;
 flag2 = flag;
 }
 QTime time;
 if (countTime) {
 time.start();
 }
 ok1 = getFaceFeature(flag1, img1, feature1, msec1);
 if (ok1) {
 emit receiveFaceFeature(flag1, feature1, msec1);
 }
 ok2 = getFaceFeature(flag2, img2, feature2, msec2);
 if (ok2) {
 emit receiveFaceFeature(flag2, feature2, msec2);
 }
 if (ok1 && ok2) {
 result = getFaceCompare(flag, feature1, feature2);
 if (countTime) {
 msec = time.elapsed() - delayms;
 } else {
 msec = delayms;
 }
 msec = msec < 0 ? 0 : msec;
 return true;
 } else {
 return false;
 }
 return false;
}
void FaceBaiDuLocal::getFaceOne(const QString &flag, const QImage &img, QString &targetName, float &result)
{
 QList feature;
 int msec;
 bool ok = getFaceFeature(flag, img, feature, msec);
 if (ok) {
 emit receiveFaceFeature(flag, feature, msec);
 getFaceOne(flag, feature, targetName, result);
 }
}
void FaceBaiDuLocal::getFaceOne(const QString &flag, const QList &feature, QString &targetName, float &result)
{
 //用当前图片的特征与特征数据库比对
 result = 0;
 int count = imgNames.count();
 for (int i = 0; i < count; i++) {
 QString imgName = imgNames.at(i);
 float currentResult = getFaceCompare(flag, feature, features.at(i));
 //qDebug() << TIMEMS << "getFaceOne" << imgName << currentResult;
 if (currentResult > result) {
 result = currentResult;
 targetName = imgName;
 }
 }
 qDebug() << TIMEMS << "getFaceOne result" << targetName << result;
}
void FaceBaiDuLocal::appendFace(const QString &flag, const QImage &img, const QString &txtFile)
{
 QList feature;
 int msec;
 QImage image = img;
 bool ok = getFaceFeature(flag, image, feature, msec);
 msleep(100);
 qDebug() << TIMEMS << "getFaceFeature result" << ok << "appendFace" << txtFile;
 if (ok) {
 emit receiveFaceFeature(flag, feature, msec);
 //保存txt文件
 QFile file(txtFile);
 if (file.open(QFile::WriteOnly)) {
 QStringList list;
 foreach (float fea, feature) {
 list.append(QString::number(fea));
 }
 file.write(list.join(",").toLatin1());
 file.close();
 }
 //保存图片文件
 QString imgName = txtFile;
 imgName = imgName.replace("txt", "jpg");
 image.save(imgName, "jpg");
 imgNames.append(QFileInfo(imgName).fileName());
 features.append(feature);
 }
}
void FaceBaiDuLocal::deleteFace(const QString &flag)
{
 //从图片名称中找到标识符
 int index = imgNames.indexOf(flag);
 if (index >= 0) {
 imgNames.removeAt(index);
 features.removeAt(index);
 //删除图片文件
 QString imgFileName = QString("%1/face/%2.jpg").arg(qApp->applicationDirPath()).arg(flag);
 QFile imgFile(imgFileName);
 imgFile.remove();
 qDebug() << TIMEMS << "delete faceImage" << imgFileName;
 //删除特征文件
 QString txtFileName = QString("%1/face/%2.txt").arg(qApp->applicationDirPath()).arg(flag);
 QFile txtFile(txtFileName);
 txtFile.remove();
 qDebug() << TIMEMS << "delete faceTxt" << txtFileName;
 }
}

Tag标签:

小编推荐:欲学习电脑技术、系统维护、网络管理、编程开发和安全攻防等高端IT技术,请 点击这里 注册账号,公开课频道价值万元IT培训教程免费学,让您少走弯路、事半功倍,好工作升职加薪!

本文出自:https://www.toutiao.com/a6601644987425751566/

免责声明:本站系公益性非盈利IT技术普及网,本文由投稿者转载自互联网的公开文章,文末均已注明出处,其内容和图片版权归原网站或作者所有,文中所述不代表本站观点,若有无意侵权或转载不当之处请从网站右下角联系我们处理,谢谢合作!


鲜花

握手

雷人

路过

鸡蛋

相关阅读

最新评论

 最新
返回顶部