Qt+百度API实现人脸对比寻找明星脸

一、要求

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】qt操作摄像头_Jason~shen的博客-CSDN博客_qt调用摄像头先上效果图左上角显示摄像头下拉选项,右上角分别是打开摄像头、关闭摄像头、截图保存的功能,左下显示摄像头内容,右下显示截图图片如何实现?1、在工程文件中添加,QT += multimedia(多媒体),QT += multimediawidgets2、主要用到的部件3、获取摄像头信息 //读取所有摄像头信息 QList<QCameraI...https://blog.csdn.net/qq_40602000/article/details/98875026

三、效果展示

 

源码下载qt+百度API实现人脸对比寻找明星脸-C++文档类资源-CSDN下载icon-default.png?t=M3K6https://download.csdn.net/download/qq_40602000/85287399

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jason~shen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值