第三章 人脸识别与信息抓取
1、主要内容:
在人脸识别基础上加一个矩形框,以及兔耳朵。
2、前期准备
需要前期准备一张兔子耳朵头像挂件图片(背景透明那种,没有就抠图),然后放在工程目录下,最好是新建一个专门放图片的文件夹img,把图片放入img文件夹中。
3、插入兔耳朵图片
新建文件,如下图,然后命名为tuer.png。
前缀太长了,可以删一部分,只留一个根目录/,然后点击Add Files,找到图片添加进来。
qt0402.cpp源代码
#include "qt0402.h"
#include "ui_qt0402.h"
qt0402::qt0402(QWidget *parent)
: QWidget(parent)
, ui(new Ui::qt0402)
, faceInfo()
{
ui->setupUi(this);
faceInfo.setIsable(false);
//显示了所有摄像头信息
const QList<QCameraInfo> cameraInfoList = QCameraInfo::availableCameras();
for (const QCameraInfo &tmpCam:cameraInfoList) {
qDebug() << tmpCam.deviceName()<< "|||" << tmpCam.description();
ui->comboBox->addItem(tmpCam.description());
}
connect(ui->comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
[=](int index){
qDebug() << cameraInfoList.at(index).description();
refreshTimer->stop();
camera->stop();
camera->deleteLater();
camera = new QCamera(cameraInfoList.at(index));
imageCapture->deleteLater();
imageCapture = new QCameraImageCapture(camera);
camera->setViewfinder(finder);
camera ->setCaptureMode(QCamera::CaptureStillImage);
imageCapture->setCaptureDestination(QCameraImageCapture::CaptureToBuffer);
connect(imageCapture, &QCameraImageCapture::imageCaptured,
this, &qt0402::showPic);
camera->start();
refreshTimer->start(40);
});
//摄像头配置
camera = new QCamera(cameraInfoList.at(0));
finder = new QCameraViewfinder();
imageCapture = new QCameraImageCapture(camera);
camera->setViewfinder(finder);
camera ->setCaptureMode(QCamera::CaptureStillImage);
imageCapture->setCaptureDestination(QCameraImageCapture::CaptureToBuffer);
//拍照与按钮绑定
connect(imageCapture, &QCameraImageCapture::imageCaptured,
this, &qt0402::showPic);
camera->start();
//定时器刷新拍照
refreshTimer = new QTimer(this);
connect(refreshTimer, &QTimer::timeout,
this, [=](){
imageCapture->capture();
});
//开Timer
refreshTimer->start(40);
//设置布局
QVBoxLayout *vboxl = new QVBoxLayout;
vboxl->addWidget(ui->label);
vboxl->addWidget(ui->pushButton);
//竖直盒子
QVBoxLayout *vboxr = new QVBoxLayout;
vboxr->addWidget(ui->comboBox);
vboxr->addWidget(finder);
vboxr->addWidget(ui->textBrowser);
//水平盒子
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addLayout(vboxl);
hbox->addLayout(vboxr);
//最终以水平布局为准hbox
this->setLayout(hbox);
imgManager = new QNetworkAccessManager(this);
connect(imgManager, &QNetworkAccessManager::finished,
this, [&](QNetworkReply *replay){
if (replay->error() != QNetworkReply::NoError) {
qDebug() << replay->errorString();
return ;
}
QByteArray replayData = replay->readAll();
qDebug() << replayData;
QJsonParseError jsonErr;
//字节数组转成json文档
QJsonDocument doc = QJsonDocument::fromJson(replayData, &jsonErr);
if (jsonErr.error != QJsonParseError::NoError) {
qDebug() << "json parse error" << jsonErr.errorString();
return ;
}
QJsonObject obj = doc.object();
//QJsonObject resultObj = obj.value("result").toObject();
//if (!resultObj.contains("face_num")) return ;
if (!obj.contains("result")) return ;
QJsonObject resultObj = obj.value("result").toObject();
//人脸数目必须大于1
if (!resultObj.contains("face_num")) return ;
if (resultObj.value("face_num").toInt() < 1) return ; //人脸数<1直接返回
if (!resultObj.contains("face_list")) return ;
QJsonArray faceList = resultObj.value("face_list").toArray();
//人脸位置信息在location里面
QJsonObject faceInfoObj = faceList[0].toObject();
if (!faceInfoObj.contains("location")) return ;
QJsonObject locationObj = faceInfoObj.value("location").toObject();
//获得四个位置信息
if (!locationObj.contains("left")) return ;
faceInfo.setLeft(locationObj.value("left").toDouble());
if (!locationObj.contains("top")) return ;
faceInfo.setTop(locationObj.value("top").toDouble());
if (!locationObj.contains("width")) return ;
faceInfo.setWidth(locationObj.value("width").toDouble());
if (!locationObj.contains("height")) return ;
faceInfo.setHeight(locationObj.value("height").toDouble());
qDebug() << faceInfo.getLeft();
qDebug() << faceInfo.getTop();
qDebug() << faceInfo.getWidth();
qDebug() << faceInfo.getHeight();
if (!faceInfoObj.contains("age")) return ;
this->faceInfo.setAge(faceInfoObj.value("age").toInt());
//人脸信息获取
QJsonObject tmpObj;
if (!faceInfoObj.contains("face_shape")) return ;
tmpObj = faceInfoObj.value("face_shape").toObject();
if (!tmpObj.contains("type")) return ;
this->faceInfo.setFace_shape(tmpObj.value("type").toString());
if (!faceInfoObj.contains("gender")) return ;
tmpObj = faceInfoObj.value("gender").toObject();
if (!tmpObj.contains("type")) return ;
this->faceInfo.setGender(tmpObj.value("type").toString());
if (!faceInfoObj.contains("glasses")) return ;
tmpObj = faceInfoObj.value("glasses").toObject();
if (!tmpObj.contains("type")) return ;
this->faceInfo.setGlasses(tmpObj.value("type").toString());
if (!faceInfoObj.contains("emotion")) return ;
tmpObj = faceInfoObj.value("emotion").toObject();
if (!tmpObj.contains("type")) return ;
this->faceInfo.setEmotion(tmpObj.value("type").toString());
if (!faceInfoObj.contains("face_type")) return ;
tmpObj = faceInfoObj.value("face_type").toObject();
if (!tmpObj.contains("type")) return ;
this->faceInfo.setFace_type(tmpObj.value("type").toString());
if (!faceInfoObj.contains("mask")) return ;
tmpObj = faceInfoObj.value("mask").toObject();
if (!tmpObj.contains("type")) return ;
this->faceInfo.setMask(tmpObj.value("type").toInt());
if (!faceInfoObj.contains("beauty")) return ;
this->faceInfo.setBeauty(faceInfoObj.value("beauty").toDouble());
faceInfo.setIsable(true);
//需要展示的内容,字符串展示append();
QString faceStr;
//年龄是int,需要转成string
faceStr.append("年龄:").append(QString::number(faceInfo.getAge())).append("\n");
faceStr.append("性别:").append(faceInfo.getGender()).append("\n");
faceStr.append("戴眼镜:").append(faceInfo.getGlasses()).append("\n");
faceStr.append("情绪:").append(faceInfo.getEmotion()).append("\n");
faceStr.append("脸型:").append(faceInfo.getFace_shape()).append("\n");
faceStr.append("戴口罩:").append(faceInfo.getMask()==1?"了戴":"没戴").append("\n");
faceStr.append("颜值:").append(QString::number(faceInfo.getBeauty())).append("\n");
ui->textBrowser->setText(faceStr);
});
//设置按钮点击操作
//[](){}这是lamda函数,也就是槽的定义,[]里=或者&都可以
connect(ui->pushButton, &QPushButton::clicked,
this, [&]() {
//进行人脸检测
//QThread;
//qDebug() << 1;
//进行图片转码,把图片转成base64编码,百度AI
QByteArray ba;
QBuffer buff(&ba);
faceImg.save(&buff, "png");
//1和2之间卡
//qDebug() << 2;
QString b64str = ba.toBase64();
//qDebug() << b64str; //特长一串
//imageCapture->capture();
//组装请求体
QJsonObject postJson;
postJson.insert("image", b64str);
postJson.insert("image_type", "BASE64");
postJson.insert("face_field", "age,face_shape,gender,glasses,emotion,face_type,mask,beauty");
QJsonDocument doc;
doc.setObject(postJson);
QByteArray postData = doc.toJson(QJsonDocument::Compact); //一行字符串,没有换行
//组装请求包
QUrl url("https://aip.baidubce.com/rest/2.0/face/v3/detect");
QUrlQuery query;
query.addQueryItem("access_token", accessToken);
url.setQuery(query);
QNetworkRequest req;
req.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
req.setUrl(url);
req.setSslConfiguration(sslConfig);
imgManager->post(req, postData);
});
//支持https协议
tokenManager = new QNetworkAccessManager(this);
qDebug() << tokenManager->supportedSchemes();
if (QSslSocket::supportsSsl()) {
qDebug() << "supporte ssl";
} else {
qDebug() << "not support ssl";
}
//发起token请求
QUrl url("https://aip.baidubce.com/oauth/2.0/token");
QUrlQuery query; //QUrlQuery提供了一个操纵键值对的方式,;
query.addQueryItem("grant_type", "client_credentials"); //通过addQueryItem添加 key 和 value
query.addQueryItem("client_id", "MEEgXrGBVMFeL8NoErsSxaZi");
query.addQueryItem("client_secret", "cNNHbQiqMWSUBHKI3CoofDO8tXyMW6GN");
url.setQuery(query);
qDebug() << url;
//配置SSL
sslConfig = QSslConfiguration::defaultConfiguration(); //通过defaultConfiguration()获取ssl的默认配置
sslConfig.setPeerVerifyMode(QSslSocket::QueryPeer);
sslConfig.setProtocol(QSsl::TlsV1_2);
//配置token请求
QNetworkRequest req;
req.setUrl(url);
req.setSslConfiguration(sslConfig);
tokenManager = new QNetworkAccessManager(this);
connect(tokenManager, &QNetworkAccessManager::finished,
this, [=](QNetworkReply *replay){
//处理应答
if (replay->error() != QNetworkReply::NoError) {
qDebug() << replay->errorString();
return ;
}
QByteArray replayData = replay->readAll();
//qDebug() << replayData;
QJsonParseError jsonErr;
//字节数组转成json文档
QJsonDocument doc = QJsonDocument::fromJson(replayData, &jsonErr);
if (jsonErr.error != QJsonParseError::NoError) {
qDebug() << "json parse error" << jsonErr.errorString();
return ;
}
QJsonObject obj = doc.object();
if (!obj.contains("access_token")) {
qDebug() << "no key:" << "access_token";
return ;
}
accessToken = obj.value("access_token").toString();
ui->textBrowser->setText(accessToken);
});
//发送请求
tokenManager->get(req);
}
qt0402::~qt0402()
{
delete ui;
}
void qt0402::takePicture(){
imageCapture->capture();
}
void qt0402::showPic(int id, const QImage &preview){
Q_UNUSED(id);
faceImg = preview;
if (faceInfo.getIsable()) {
//在头像画个框
QPainter p(&faceImg); //指定在preview画
p.setPen(Qt::red);
p.drawRect(faceInfo.getLeft(),
faceInfo.getTop(),
faceInfo.getWidth(),
faceInfo.getHeight());
//tuer.png 396*272
p.drawPixmap(
faceInfo.getLeft()+faceInfo.getWidth()/2-396/3,
faceInfo.getTop()-350,
396,272,
QPixmap(":/img/tuer.png"),
0,0,0,0);
}
ui->label->setPixmap(QPixmap::fromImage(faceImg)); //图形转换
}
faceinfo.h源代码
#ifndef FACEINFO_H
#define FACEINFO_H
#include <QString>
class faceinfo
{
private:
bool isable;
int mask;
int age;
double left;
double top;
double height;
double width;
double beauty;
QString face_shape;
QString gender;
QString glasses;
QString emotion;
QString face_type;
public:
faceinfo();
int getMask() const;
void setMask(int newMask);
int getAge() const;
void setAge(int newAge);
double getLeft() const;
void setLeft(double newLeft);
double getTop() const;
void setTop(double newTop);
double getHeight() const;
void setHeight(double newHeight);
double getWidth() const;
void setWidth(double newWidth);
double getBeauty() const;
void setBeauty(double newBeauty);
const QString &getFace_shape() const;
void setFace_shape(const QString &newFace_shape);
const QString &getGender() const;
void setGender(const QString &newGender);
const QString &getGlasses() const;
void setGlasses(const QString &newGlasses);
const QString &getEmotion() const;
void setEmotion(const QString &newEmotion);
const QString &getFace_type() const;
void setFace_type(const QString &newFace_type);
bool getIsable() const;
void setIsable(bool newIsable);
};
#endif // FACEINFO_H
在faceinfo.h文件中,在private下定义各个参数,如下
int age;
double beauty;
QString face_shape;
理论上来说同类型的参数放在一起,小的(int)放在前面,节省空间,提升效率。
构建类的set 、get方法,右键reactor,选择第二个,然后勾选前两个getter和setter,就自动把所有参数都选上了。
在faceinfo.cpp中就生成了代码,并且头文件和函数声明都定义好了,节省了时间。
qt0402.h源代码
#ifndef QT0402_H
#define QT0402_H
#include <QWidget>
#include <QNetworkReply>
#include <QWidget>
#include <QDebug>
#include <QString>
#include <QCamera>
#include <QCameraInfo>
#include <QCameraViewfinder>
#include <QCameraImageCapture>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QTimer>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QUrlQuery>
#include <QSslConfiguration>
#include <QSslSocket>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QBuffer>
#include <QJsonValue>
#include <QJsonArray>
#include <faceinfo.h>
#include <QPainter>
#include <QThread>
//1、当前路径找头文件<>,最快的
//2、gcc xxx -I xxx自定义头文件路径
//3、系统设置的(头文件)路径
QT_BEGIN_NAMESPACE
namespace Ui { class qt0402; }
QT_END_NAMESPACE
class qt0402 : public QWidget
{
Q_OBJECT
public:
qt0402(QWidget *parent = nullptr);
~qt0402();
void showPic(int id, const QImage &preview);
private:
Ui::qt0402 *ui;
QCamera *camera;
QCameraViewfinder *finder;
QCameraImageCapture *imageCapture;
QTimer *refreshTimer;
void takePicture();
QSslConfiguration sslConfig;
QNetworkAccessManager *tokenManager;
QNetworkAccessManager *imgManager;
QString accessToken;
QImage faceImg;
faceinfo faceInfo;
};
#endif // QT0402_H
附上兔子耳朵图片