一、要求
1、使用百度人脸识别库
2、识别对比图片,获取图片相似度
3、显示最相似的明星照片、显示本人照片
4、调用摄像头拍照、比对查找最相似的明星脸
5、录入明星照片、查看照片列表、修改、删除照片
二、实现方式
2.1百度API封装
1、获取Token
鉴权认证参考:http://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjhhu
填写API_Key和Secret_Key,运行程序后会自动读取,并保存在本地
2、配置https访问
需要依赖libcrypto-1_1.dll和libssl-1_1.dll库,可以在qt路径下找到,放在与exe同级目录下
编译器要选择32位的,62位无效
void BaiduFaceOnline::https_ssl_config(QNetworkRequest& NetworkRequest)
{
QSslConfiguration config = NetworkRequest.sslConfiguration();
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::TlsV1SslV3);
NetworkRequest.setSslConfiguration(config);
}
3、相似度比对
访问API,https://aip.baidubce.com/rest/2.0/face/v3/match
使用QEventLoop阻塞等待响应结果
QNetworkReply * reply = NetAccManager->post(NetRequest, post_param);
QEventLoop eventLoop;
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
4、QImage转base64编码字符
图片的格式统一位为jpg
QString BaiduFaceOnline::image2base64_str(const QImage& img)
{
QByteArray data;
QBuffer buf(&data);
img.save(&buf, "jpg");
QString b64str = QString(data.toBase64());
buf.close();
return b64str;
}
baidufaceonline.h
#ifndef BAIDUFACEONLINE_H
#define BAIDUFACEONLINE_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QUrl>
#include <QNetworkReply>
#include <QMessageBox>
#include <QImage>
#include <QFile>
#include <QTextStream>
#include <QBuffer>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>
class BaiduFaceOnline : public QObject
{
Q_OBJECT
public:
explicit BaiduFaceOnline(QObject *parent = nullptr);
void https_ssl_config(QNetworkRequest& NetworkRequest);
QString image2base64_str(const QImage& img);
QImage base64_str2image(const QString & base64_str);
void writeAccToken(const QString& token);
QString readAccToken();
public slots:
void getAccToken();
double FaceMatch(const QImage& image1, const QImage& image2);
private slots:
void getAccTokenReply(QNetworkReply* reply);
double getFaceMatchReply(QNetworkReply* reply);
private:
QNetworkAccessManager* NetAccManager;
QNetworkRequest NetRequest;
/* 应用参数 */
const QString API_Key = "填写自己的";
const QString Secret_Key = "填写自己的";
QString AccToken = "";
/* 应用参数end */
const QString FaceMatchUrl = "https://aip.baidubce.com/rest/2.0/face/v3/match";
};
#endif // BAIDUFACEONLINE_H
baidufaceonline.cpp
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
#include "baidufaceonline.h"
#include <QEventLoop>
BaiduFaceOnline::BaiduFaceOnline(QObject *parent) : QObject(parent)
{
NetAccManager = new QNetworkAccessManager(this);
https_ssl_config(NetRequest);
AccToken = readAccToken();
qDebug() << "AccToken" <<AccToken;
getAccToken();
}
//配置https访问,win32依赖libcrypto-1_1.dll和libssl-1_1.dll,放在与exe同级目录下
void BaiduFaceOnline::https_ssl_config(QNetworkRequest& NetworkRequest)
{
QSslConfiguration config = NetworkRequest.sslConfiguration();
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::TlsV1SslV3);
NetworkRequest.setSslConfiguration(config);
}
//QImage转base64编码字符串
QString BaiduFaceOnline::image2base64_str(const QImage& img)
{
QByteArray data;
QBuffer buf(&data);
img.save(&buf, "jpg");
QString b64str = QString(data.toBase64());
buf.close();
return b64str;
}
//base64编码字符串转QImage
QImage BaiduFaceOnline::base64_str2image(const QString & base64_str)
{
QImage image;
QByteArray base64_data = base64_str.toLatin1();
image.loadFromData(QByteArray::fromBase64(base64_data));
return image;
}
void BaiduFaceOnline::writeAccToken(const QString& token)
{
QFile f("AccToken.txt");
if(!f.open(QIODevice::WriteOnly | QIODevice::Text))
{
qDebug() << ("打开文件失败");
}
QTextStream txtOutput(&f);
txtOutput << token;
f.close();
}
QString BaiduFaceOnline::readAccToken()
{
QFile f("AccToken.txt");
if(!f.open(QIODevice::ReadOnly | QIODevice::Text))
{
qDebug() << ("打开文件失败");
return "";
}
QTextStream txtInput(&f);
QString lineStr;
lineStr = txtInput.readLine();
f.close();
return lineStr;
}
//获取token
void BaiduFaceOnline::getAccToken()
{
//鉴权认证参考:http://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjhhu
if(AccToken.isEmpty()) {
QStringList parlist;
parlist.append(QString("grant_type=%1").arg("client_credentials"));
parlist.append(QString("client_id=%1").arg(API_Key));
parlist.append(QString("client_secret=%1").arg(Secret_Key));
QByteArray parameters = parlist.join("&").toUtf8();
QUrl url("https://aip.baidubce.com/oauth/2.0/token");
NetRequest.setUrl(url);
NetRequest.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
connect(NetAccManager,&QNetworkAccessManager::finished,this,&BaiduFaceOnline::getAccTokenReply);
NetAccManager->post(NetRequest,parameters);
}
}
//获取token槽
void BaiduFaceOnline::getAccTokenReply(QNetworkReply* reply)
{
QString error = reply->errorString();
if (!error.isEmpty() && error != "Unknown error") {
return;
}
if (reply->error() != QNetworkReply::NoError) {
QMessageBox::warning(0,"","请求错误!");
return;
}
else {
QByteArray content = reply->readAll();
reply->deleteLater();
QJsonParseError jsonError;
QJsonDocument doucment = QJsonDocument::fromJson(content, &jsonError); // 转化为 JSON 文档
if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)){ // 解析未发生错误
if (doucment.isObject()){ // 文档只有一个json对象
QJsonObject object = doucment.object(); // 转化为对象
if (object.contains("access_token")){ // 包含指定的 key
QJsonValue value = object.value("access_token"); // 获取指定 key 对应的 value
if (value.isString()){ // 判断 value 是否为字符串
AccToken = value.toString(); // 将 value 转化为字符串
qDebug()<< "parse AccToken:"<<AccToken;
writeAccToken(AccToken); //写入txt文件
}
}
}
}
}
disconnect(NetAccManager,&QNetworkAccessManager::finished,this,&BaiduFaceOnline::getAccTokenReply);
}
//人脸对比
double BaiduFaceOnline::FaceMatch(const QImage& image1, const QImage& image2)
{
qDebug() <<image1;
qDebug() <<image2;
QString img1_base64 = image2base64_str(image1);
QString img2_base64 = image2base64_str(image2);
QJsonObject post_data1;
post_data1.insert("image", img1_base64);
post_data1.insert("image_type", "BASE64");
QJsonObject post_data2;
post_data2.insert("image", img2_base64);
post_data2.insert("image_type", "BASE64");
QJsonArray img_array;
img_array.append(post_data1);
img_array.append(post_data2);
QJsonDocument document;
document.setArray(img_array);
QByteArray post_param = document.toJson(QJsonDocument::Indented);
qDebug() <<post_param;
QUrl url(FaceMatchUrl + "?access_token=" + AccToken);
NetRequest.setUrl(url);
NetRequest.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
QNetworkReply * reply = NetAccManager->post(NetRequest, post_param);
QEventLoop eventLoop;
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
double score = getFaceMatchReply(reply);
qDebug() <<"score:" << score;
return score;
}
double BaiduFaceOnline::getFaceMatchReply(QNetworkReply* reply)
{
double score = 0;
qDebug() <<"reply";
QString error = reply->errorString();
if (!error.isEmpty() && error != "Unknown error")
{
return score;
}
if (reply->error() != QNetworkReply::NoError)
{
qDebug() <<"请求错误!";
return score;
}
else
{
QByteArray content = reply->readAll();
QJsonParseError jsonError;
QJsonDocument doucment = QJsonDocument::fromJson(content, &jsonError); // 转化为 JSON 文档
if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError))
{
if (doucment.isObject())
{
QJsonObject root_object = doucment.object();
if (root_object.take("error_code").toInt() == 0 && root_object.take("error_msg").toString() == "SUCCESS")
{
QJsonObject result_object = root_object.take("result").toObject();
score = result_object.take("score").toDouble(); /*相似度: */
}
}
}
reply->deleteLater();
}
return score;
}
2.2 人脸搜索并找出最相似的
该任务比较耗时,放入线程中执行,采用moveToThread的方式
// 调用 moveToThread 将该任务交给 workThread
auto *searchWorker = new SearchWorker ;
searchWorker->moveToThread(&workerThread);
// operate 信号发射后启动线程工作
connect(this, SIGNAL(operate()), searchWorker, SLOT(doWork()));
// 该线程结束时销毁
connect(&workerThread, &QThread::finished, searchWorker, &QObject::deleteLater);
// 线程结束后发送信号,对结果进行处理
connect(searchWorker, &SearchWorker::resultReady, this, &CameraWidget::slot_handleResults);
// 启动线程
workerThread.start();
seachWorker封装
#ifndef SEARCHWORKER_H
#define SEARCHWORKER_H
#include <QObject>
class BaiduFaceOnline;
class SearchWorker: public QObject
{
Q_OBJECT
public:
explicit SearchWorker(QObject *parent = nullptr);
public slots:
void doWork();
signals:
/**
* @brief resultReady 线程完成工作时发送的信号
* @param score 相似度
* @param name 图片名称
*/
void resultReady(double score, const QString &name);
private:
BaiduFaceOnline *m_baiduFace;
};
#endif // SEARCHWORKER_H
#include "baidufaceonline.h"
#include "searchworker.h"
#include <QDir>
SearchWorker::SearchWorker(QObject *parent)
:QObject(parent)
{
m_baiduFace = new BaiduFaceOnline(this);
}
void SearchWorker::doWork()
{
/* 读取文件 */
QString path = "./faces";
QDir dir(path);
if (!dir.exists()) {
return;
}
// 设置过滤器
dir.setFilter(QDir::Files | QDir::NoSymLinks);
QStringList filters;
filters << "*.jpg";
dir.setNameFilters(filters);
QStringList imgList = dir.entryList();
if (imgList.count() <= 0) {
return;
}
// 创建单元项
double maxScore = 0; /* 最大分数 */
QString maxScoreName;
for (int i = 0; i<imgList.count(); ++i) {
QImage face1 = QImage("./m.jpg");
QImage face2 = QImage(path + "/" + imgList.at(i));
qDebug() <<face1.isNull() <<face2.isNull();
double score = m_baiduFace->FaceMatch(face1, face2);
if(score>=maxScore)
{
maxScore = score;
maxScoreName = imgList.at(i);
}
}
qDebug() <<"end";
qDebug() <<maxScoreName;
emit resultReady(maxScore, maxScoreName);
}
2.3摄像头采集
可以查看我另一篇文章,使用qt自带封装的摄像头类QCamera、QCameraInfo、QCameraViewfinder、QCameraImageCapture
三、效果展示
源码下载qt+百度API实现人脸对比寻找明星脸-C++文档类资源-CSDN下载https://download.csdn.net/download/qq_40602000/85287399