qt5.8(c++)实现阿里云人脸识别云接口

简介: 阿里云提供了人脸识别的api,其示例除了c/c++,其他主流语言都有相应的实例。 本人由于项目债务和集成需要,需要用c/c++实现,若只是支持win/Linux平台,采用 acl_master源码库也可行,有需要的可参考《阿里云短信服务接口的c++实现》, 阿里云的短信服务接口与人脸识别在数据签名、加密等方面是一致的。

阿里云提供了人脸识别的api,其示例除了c/c++,其他主流语言都有相应的实例。

本人由于项目债务和集成需要,需要用c/c++实现,若只是支持win/Linux平台,采用

acl_master源码库也可行,有需要的可参考《阿里云短信服务接口的c++实现》,

阿里云的短信服务接口与人脸识别在数据签名、加密等方面是一致的。

我当前项目由于跨平台支持android编译,所以采用了qt实现阿里云的人脸识别接口。

备注:若需要静态编译实现阿里云的人脸识别,可能 qt环境需要重新静态编译network模块,追加openssl静态编译支持,

更细节的实现可以参考《qt5.8_for_vs2015 and openssl静态编译 》。

阿里云的人脸识别主要有两个难点,其一是,若你直接传输图片内容,需要对图片内容进行编码,其二是需要实现签名认证。

下面就如何实现阿里云的人脸属性识别的过程(人脸检测定位、人脸比对类似):

1)首先需要开通 阿里云 的人脸识别服务,下面是本人在阿里云的云产品通用代金券链接:

https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=pfb80n4a

开通服务后,进入 人脸识别的控制台,在其左侧栏目有个API调试,点击进去,这里标注的

另外 Access Key ID和Access Key Secret在API调用也需要,在你的头像下的AccessKeys项点击进去获取即可。

2)阿里API说明手册指出,若图片内容指定url,需要你只身配备网络存储路径,采用阿里的OSS也是不错的选择,若直接传输图片内容,需要采用base64编码,刚好qt5.8就能直接实现,下面就直接传输本地图片内容进行阿里云人脸识别API调用,记得qt工程文件需要添加,

QT += network
AI 代码解读

示例代码:

QByteArray  FaceDetectObject::Image_To_Base64(QString image_path)
{
    QImage image(image_path);
    QByteArray ba;
    QBuffer buf(&ba);
    image.save(&buf,"jpg");
    QByteArray hexed = ba.toBase64();
    buf.close();
    return hexed;
}
AI 代码解读

 

3)关于人脸识别的通信接口的签名的细节要求查看官方说明文档《API校验规范》章节,其内容要求UTF-8和Base64编码,签名算法遵循RFC 2104HMAC-SHA1规范。

MD5转换示例代码:

QByteArray FaceDetectObject::getMD5(QByteArray bytes_)
{
    QCryptographicHash ch(QCryptographicHash::Md5);
    QByteArray ret;
    ch.addData(bytes_);
    ret = ch.result();
    return ret;
}
AI 代码解读

 

HMACSha1算法示例代码:
AI 代码解读
QByteArray FaceDetectObject::HMACSha1(QByteArray key, QByteArray baseString)
 {
     int blockSize = 64;                // HMAC-SHA-1 block size, defined in SHA-1 standard
     if (key.length() > blockSize) {    // if key is longer than block size (64), reduce key length with SHA-1 compression
         key = QCryptographicHash::hash(key, QCryptographicHash::Sha1);
     }
     QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
     QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "/"
     // ascii characters 0x36 ("6") and 0x5c ("/") are selected because they have large
     // Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
     for (int i = 0; i < key.length(); i++) {
         innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
         outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
     }
     // result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64
     QByteArray total = outerPadding;
     QByteArray part = innerPadding;
     part.append(baseString);
     total.append(QCryptographicHash::hash(part, QCryptographicHash::Sha1));
     QByteArray hashed = QCryptographicHash::hash(total, QCryptographicHash::Sha1);
          /// 注意——>把字符串hashed转换为Hex,内存中的ASCII码arrayFromHexString
     QByteArray arrayFromHexString = QByteArray::fromHex(hashed.toHex());
     qDebug()<<"hmacSha1内存中的ASCII码 arrayFromHexString \n"<
AI 代码解读

4)阿里云一般对于http协议的时间格式要求是GMT的

也就是QT里面的UTC格式, 实现 样例:

QLocale lo = QLocale::English;//设置QLocale为英文
    QString date = lo.toString(QDateTime::currentDateTimeUtc(),"ddd, dd MMM yyyy hh:mm:ss")+QString(" GMT");
AI 代码解读

其通信协议需要openssl支持才能实现https请求,实现 样例:

    QNetworkRequest request;
    QSslConfiguration config;
    config.setPeerVerifyMode(QSslSocket::VerifyNone);
    config.setProtocol(QSsl::TlsV1_0);
    request.setSslConfiguration(config);
AI 代码解读

5)实现阿里云的人脸属性识别请求,记得把key和密钥换成自己的:

void FaceDetectObject::do_post_al(QByteArray bytearray)
{
    qDebug()<<"img_bytearray_size="<deleteLater();
    }
    reply = manager->post(request,body);
//            connect(reply, &QNetworkReply::finished, this, &FaceDetectThread::finishedReplay);
    connect(reply, SIGNAL(finished()), this, SLOT(finishedReplay()));
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
              this, SLOT(slotError(QNetworkReply::NetworkError)));
//            connect(reply, SIGNAL(sslErrors(QList)),
//                      this, SLOT(slotSslErrors(QList)));
    connect(reply,SIGNAL(downloadProgress(qint64,qint64)),
            this,SLOT(downloadProgress(qint64,qint64)));
    qDebug() << "start post_al";
}
AI 代码解读

备注:bytearray是base64的图片内容

6)请求返回的数据为JSON格式 描述,字段描述细节参考官方的人脸属性识别API 调用说明,内容有点多,我就不截图。

返回数据获取示例代码如下:

void FaceDetectObject::finishedReplay()
{
    QByteArray bytes = reply->readAll();
    const QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    reply->deleteLater();
    reply = Q_NULLPTR;
    if (!redirectionTarget.isNull()) {//如果网址跳转重新请求
        const QUrl redirectedUrl = url.resolved(redirectionTarget.toUrl());
        qDebug()<<"redirectedUrl:"<
AI 代码解读

返回结果示例:

{"face_num":1,"face_rect":[391,365,94,127],"face_prob":[1.0],"pose":[5.843783378601074,-3.454075336456299,2.8692541122436523],"landmark_num":105,"landmark":[..展示需要删除..],"iris":[417.10675048828125,415.4582214355469,4.029685020446777,460.1661071777344,418.9272155761719,4.029685020446777],"gender":[0],"age":[28],"expression":[0],"glass":[0],"dense_fea_len":1024,"dense_fea":"[..展示需要删除..]","errno":0,"request_id":"3bad9b68-b337-4c07-8661-403178948a31"}
AI 代码解读

取得返回结果后就是 业务应用的范畴了,人脸检测定位、人脸比对实现类似。基于我的业务逻辑只需要脸数、性别、年龄、笑容,示例代码如下:

//输入参数来自:QByteArray bytes = reply->readAll();
void FaceDetectObject::JsonDecode(QByteArray bytes)
{
    QJsonParseError json_error;
    QJsonDocument retVals = QJsonDocument::fromJson(bytes,&json_error);
    if(json_error.error == QJsonParseError::NoError)
    {
        if(retVals.isObject())
        {
            int face_num_val=0 ;
            QVector genders;
            QVector ages;
            QVector expressions;
            QJsonObject obj = retVals.object();
            if(obj.contains("face_num"))
            {
                QJsonValue face_num_Json = obj.value("face_num");
                qDebug() <<"face_num_Json="<< face_num_Json.toInt()<<"\n";
                if(face_num_Json.isDouble())
                {
                    face_num_val = face_num_Json.toInt();
                }
            }
            if(obj.contains("gender"))
            {
                QJsonValue gender_Json = obj.value("gender");
                if(gender_Json.isArray())
                {
                    QJsonArray gender_vals = gender_Json.toArray();
                    qDebug() <<"gender_vals="<< gender_vals<<"\n";
                    foreach (QJsonValue gval, gender_vals) {
                        if(gval.isDouble())
                            genders.push_back(gval.toInt()>0?true:false);
                    }
                    if(genders.size()!=face_num_val)
                    {
                        qDebug() << QString("gender vector num(%1) is not map face_num(%2)!")
                                    .arg(genders.size()).arg(face_num_val);
                    }

                }else{
                    qDebug() << "gender_Json value type is not map Array!";
                }
            }
            if(obj.contains("age"))
            {
                QJsonValue age_Json = obj.value("age");

                if(age_Json.isArray())
                {
                    QJsonArray age_vals = age_Json.toArray();
                    qDebug() <<"age_vals="<< age_vals<<"\n";
                    foreach (QJsonValue age_val, age_vals) {
                        if(age_val.isDouble())
                            ages.push_back(age_val.toInt());
                    }
                    if(ages.size()!=face_num_val)
                    {
                        qDebug() << QString("ages vector num(%1) is not map face_num(%2)!")
                                    .arg(ages.size()).arg(face_num_val);
                    }
                }else{
                    qDebug() << "age_Json value type is not map Array!";
                }
            }
            if(obj.contains("expression"))
            {
                QJsonValue expression_Json = obj.value("expression");
                if(expression_Json.isArray())
                {
                    QJsonArray expression_vals = expression_Json.toArray();
                    qDebug() <<"expression_vals="<< expression_vals<<"\n";
                    foreach (QJsonValue expression_val, expression_vals) {
                        if(expression_val.isDouble())
                            expressions.push_back(expression_val.toInt()>0?true:false);
                    }
                    if(expressions.size()!=face_num_val)
                    {
                        qDebug() << QString("expressions vector num(%1) is not map face_num(%2)!")
                                    .arg(expressions.size()).arg(face_num_val);
                    }
                }else{
                    qDebug() << "expression_Json value type is not map Array!";
                }
            }
            qDebug() << "face_num_val="<
AI 代码解读

下面给出本实例的完整通信接口供有需要的朋友,记得换成自己的key和密钥,由于是示例代码,很多地方未做优化和产品化考虑,请大家斟酌参考:

#ifndef FACEDETECTOBJECT_H
#define FACEDETECTOBJECT_H

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

class FaceDetectObject
        : public QObject
{
    Q_OBJECT
public:
    FaceDetectObject(QObject * parent = 0);
    ~FaceDetectObject();

private:
    QByteArray getMD5(QByteArray bytes_);
    QByteArray HMACSha1(QByteArray key, QByteArray baseString);
    QByteArray  Image_To_Base64(QString image_path);
    QImage Base64_To_Image(QByteArray bytearray,QString save_Path);
    QByteArray  Image_To_ByteArray(QString image_path);
    QImage ByteArray_To_Image(QByteArray bytearray,QString save_Path);
public slots:
    void do_get();
    void do_post_img(QString imgFile);
    void do_post(QByteArray bytearray);
    void do_post_al(QByteArray bytearray);
private slots:
    void finishedReplay();
    void slotError(QNetworkReply::NetworkError net_error);
    void downloadProgress(qint64 bytesSent, qint64 bytesTotal);
private:
    QNetworkAccessManager *manager;
    QUrl url;
    QNetworkReply   *reply;
};

#endif // FACEDETECTOBJECT_H
AI 代码解读
#include "facedetectobject.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 

FaceDetectObject::FaceDetectObject(QObject * parent)
    : QObject(parent)
{
//    //test
//    QByteArray src = Image_To_ByteArray("fdtimg.jpg");
//    QImage dest = ByteArray_To_Image(src,"fdtimg_c.jpg");
    manager =new QNetworkAccessManager(this);
    url = QUrl("http://www.baidu.com/");
    reply = Q_NULLPTR;
}

FaceDetectObject::~FaceDetectObject()
{

}

void FaceDetectObject::do_get()
{
    QNetworkRequest request;
    request.setUrl(url);
    if(reply != Q_NULLPTR) {//更改reply指向位置钱一定要保证之前的定义了自动delete
        reply->deleteLater();
    }
    reply = manager->get(request);
//            connect(reply, &QNetworkReply::finished, this, &FaceDetectThread::finishedReplay);
    connect(reply, SIGNAL(finished()), this, SLOT(finishedReplay()));
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
              this, SLOT(slotError(QNetworkReply::NetworkError)));
    connect(reply,SIGNAL(downloadProgress(qint64,qint64)),
            this,SLOT(downloadProgress(qint64,qint64)));
    qDebug() << "start get";
}

void FaceDetectObject::do_post_img(QString imgFile)
{
    qDebug()<<"do_post_img and imgfile="<deleteLater();
    }
    reply = manager->post(request,bytearray);
//            connect(reply, &QNetworkReply::finished, this, &FaceDetectThread::finishedReplay);
    connect(reply, SIGNAL(finished()), this, SLOT(finishedReplay()));
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
              this, SLOT(slotError(QNetworkReply::NetworkError)));
//            connect(reply, SIGNAL(sslErrors(QList)),
//                      this, SLOT(slotSslErrors(QList)));
    connect(reply,SIGNAL(downloadProgress(qint64,qint64)),
            this,SLOT(downloadProgress(qint64,qint64)));
    qDebug() << "start post";
}

void FaceDetectObject::do_post_al(QByteArray bytearray)
{
    qDebug()<<"img_bytearray_size="<deleteLater();
    }
    reply = manager->post(request,body);
//            connect(reply, &QNetworkReply::finished, this, &FaceDetectThread::finishedReplay);
    connect(reply, SIGNAL(finished()), this, SLOT(finishedReplay()));
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
              this, SLOT(slotError(QNetworkReply::NetworkError)));
//            connect(reply, SIGNAL(sslErrors(QList)),
//                      this, SLOT(slotSslErrors(QList)));
    connect(reply,SIGNAL(downloadProgress(qint64,qint64)),
            this,SLOT(downloadProgress(qint64,qint64)));
    qDebug() << "start post_al";
}

QByteArray FaceDetectObject::getMD5(QByteArray bytes_)
{
    QCryptographicHash ch(QCryptographicHash::Md5);
    QByteArray ret;
    ch.addData(bytes_);
    ret = ch.result();
    return ret;
}

QByteArray FaceDetectObject::HMACSha1(QByteArray key, QByteArray baseString)
 {
     int blockSize = 64;                // HMAC-SHA-1 block size, defined in SHA-1 standard
     if (key.length() > blockSize) {    // if key is longer than block size (64), reduce key length with SHA-1 compression
         key = QCryptographicHash::hash(key, QCryptographicHash::Sha1);
     }
     QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
     QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "/"
     // ascii characters 0x36 ("6") and 0x5c ("/") are selected because they have large
     // Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
     for (int i = 0; i < key.length(); i++) {
         innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
         outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
     }
     // result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64
     QByteArray total = outerPadding;
     QByteArray part = innerPadding;
     part.append(baseString);
     total.append(QCryptographicHash::hash(part, QCryptographicHash::Sha1));
     QByteArray hashed = QCryptographicHash::hash(total, QCryptographicHash::Sha1);
          /// 注意——>把字符串hashed转换为Hex,内存中的ASCII码arrayFromHexString
     QByteArray arrayFromHexString = QByteArray::fromHex(hashed.toHex());
     qDebug()<<"hmacSha1内存中的ASCII码 arrayFromHexString \n"<readAll();
    const QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    reply->deleteLater();
    reply = Q_NULLPTR;
    if (!redirectionTarget.isNull()) {//如果网址跳转重新请求
        const QUrl redirectedUrl = url.resolved(redirectionTarget.toUrl());
        qDebug()<<"redirectedUrl:"<
AI 代码解读

 

目录
打赏
0
0
0
0
2
分享
相关文章
掌握Qt和C++:构建你的第一个P2P应用程序
掌握Qt和C++:构建你的第一个P2P应用程序
370 3
|
11月前
|
深入探究Qt与C++标准的兼容之旅
深入探究Qt与C++标准的兼容之旅
981 3
C++视角下的Qt按钮:从基础应用到高级定制(二)
C++视角下的Qt按钮:从基础应用到高级定制
250 2
|
11月前
|
C++视角下的Qt按钮:从基础应用到高级定制(一)
C++视角下的Qt按钮:从基础应用到高级定制
656 2
|
11月前
|
【Qt 基本类】QDateTime类在C++中的应用与深度解析
【Qt 基本类】QDateTime类在C++中的应用与深度解析
445 0
|
11月前
|
DBus类型系统以及在Qt和C++ 中的使用(二)
DBus类型系统以及在Qt和C++ 中的使用
346 0
深入对比:Qt 的 QFile/QFileInfo 和与 C++17 Filesystem 和标准文件流 的细节剖析
深入对比:Qt 的 QFile/QFileInfo 和与 C++17 Filesystem 和标准文件流 的细节剖析
781 3
Qt(C++)开发一款图片防盗用水印制作小工具
文本水印是一种常用的防盗用手段,可以将文本信息嵌入到图片、视频等文件中,用于识别和证明文件的版权归属。在数字化和网络化的时代,大量的原创作品容易被不法分子盗用或侵犯版权,因此加入文本水印成为了保护原创作品和维护知识产权的必要手段。 通常情况下,文本水印可以包含版权声明、制作者姓名、日期、网址等信息,以帮助识别文件的来源和版权归属。同时,为了增强防盗用效果,文本水印通常会采用字体、颜色、角度等多种组合方式,使得水印难以被删除或篡改,有效地降低了盗用意愿和风险。 开发人员可以使用图像处理技术和编程语言实现文本水印的功能,例如使用Qt的QPainter类进行文本绘制操作,将文本信息嵌入到图片中,
272 1
视觉智能开放平台产品使用合集之uniapp框架如何使用阿里云金融级人脸识别
视觉智能开放平台是指提供一系列基于视觉识别技术的API和服务的平台,这些服务通常包括图像识别、人脸识别、物体检测、文字识别、场景理解等。企业或开发者可以通过调用这些API,快速将视觉智能功能集成到自己的应用或服务中,而无需从零开始研发相关算法和技术。以下是一些常见的视觉智能开放平台产品及其应用场景的概览。
212 0
Qt(C++)使用QChart静态显示3个设备的温度变化曲线
QChart模块是Qt Charts库的基础,提供了用于创建和显示各种类型图表的类和接口。Qt Charts库是一个功能丰富、易于使用的数据可视化工具库,可以帮助开发者在应用程序中添加漂亮而又交互性强的图表。
130 1
Qt(C++)使用QChart静态显示3个设备的温度变化曲线