- 前言:基于QT平台开发的一款人脸口罩检测系统,利用百度AI-EasyDL平台搭建深度学习模型并通过api调用。
准备工作
- 1、登录百度AI-EasyDL平台,注册账号,按照帮助文档新建模型
- 2、按照帮助文档建立数据集,并给数据集标注(这里可以直接上传未标注的数据集-图片,然后跟着说明一张一张图片标注)
- 3、为模型添加数据集、训练模型、校验、发布。
- 4、点击模型页“服务详情”->立即使用即可获取到API Key和Secret Key保存备用。
数据集:
![](https://i-blog.csdnimg.cn/blog_migrate/80202c85018a0b1665a89a4e457942dc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/50b538898cc8bcaa848155b84441fe1b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/45aa8b46faa9a90bb20d196a9b5e5612.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c81283884a1b293ab5d3a3d27804b588.png)
- 5、布局更改:在上一篇文章布局的基础上添加两个按钮:“打开文件”与“发送请求”,布局在原先的基础上稍微修改一下,如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/6bddc770d7c057510f5ea8009cbc24d5.png)
- 6、添加以上按钮的槽函数(这里不直接右击按钮转到槽函数,而是在代码里使用connect机制)与发送请求的接收数据函数:
![](https://i-blog.csdnimg.cn/blog_migrate/b26459d2d477cc570856287036b4f910.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e814245a0492989bea43f830107b6f46.png)
- 7、在构造函数中设置request按钮的文本和不可点击属性,在截图按钮的槽函数中打开request按钮的可点击属性并指定其槽函数
//设置request按钮的文本显示
ui->request->setText("获取AccessToken");
ui->request->setDisabled(true);
//设置request按钮的可点击属性
ui->request->setEnabled(true);
getAccessToken = connect(ui->request, SIGNAL(clicked()), this, SLOT(on_getAccessToken()));
获取AccessToken
- 点击获取AccessToken按钮即可触发请求函数。请求函数主要内容是设置apiKey和secretKey的内容,然后按照api文档设置请求头,以及url,最后绑定该请求对应的接收函数即可。这里在使用net相关功能时,.pro文件里需要添加QT += network来支持net应用开发。
//获取AccessToken
void MainWindow::on_getAccessToken(){
QString apiKey = "x7AfM65W69R5Z6fY8wVYvOct";//替换为个人的apiKey
QString secretKey = "qw1eG**************AV6d7oNM";//替换为个人的secretKey
QNetworkRequest request;
request.setRawHeader("Content-Type", "application/json");
request.setRawHeader("charset", "UTF-8");
QUrl url("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=" + apiKey + "&client_secret=" + secretKey);
request.setUrl(url);
QByteArray data;
get_accessToken = new QNetworkAccessManager(this);
connect(get_accessToken, SIGNAL(finished(QNetworkReply*)), this, SLOT(accessTokenResult(QNetworkReply*)));
get_accessToken->post(request, data);
}
- 返回结果接收函数主要是将接收到的结果进行json解析,获取其中的access_token值即可,如若收到的值没有错误,则进行修改request按钮的功能函数,修改显示文本、disconnect和重新connect。
//accessToken结果接收
void MainWindow::accessTokenResult(QNetworkReply* pReply){
if(pReply->error() == QNetworkReply::NoError){
QByteArray string = pReply->readAll();
//获取内容
QJsonParseError jsonErr;
QJsonDocument jsonDoc = QJsonDocument::fromJson(string, &jsonErr);
if (jsonDoc.isObject()){
QJsonObject jsonObj = jsonDoc.object();
accessToken = jsonObj.take("access_token").toString();
qDebug()<<"access_token:"+accessToken;
//替换request按钮功能
disconnect(getAccessToken);
ui->request->setText("识别");
connect(ui->request, SIGNAL(clicked()), this, SLOT(on_recognize()));
}
}
}
将获取到的图片转为Base64编码
- 首先在.h文件里添加私有变量QString imageDatas、公有函数void imageToBase64(QImage image);然后在.cpp文件中具体实现。
void MainWindow::imageToBase64(QImage image){
//image to base64
QByteArray imageData;
QBuffer buff(&imageData);
image.save(&buff, "jpg");
imageDatas = imageData.toBase64();
}
识别
- 首先判断imageDatas和accessToken是否为空,然后再进行发送请求步骤,在发送请求时需要按照api文档要求的json形式发送(此处需要转换),接着绑定接收数据的函数。
//识别
void MainWindow::on_recognize(){
if (imageDatas.isEmpty())
{
qDebug() << "image base64 empty";
return;
}
if (accessToken.isEmpty())
{
qDebug() << "accessToken empty";
return;
}
QUrl url;
url.setUrl("https://aip.baidubce.com/rpc/2.0/ai_custom/v1/classification/moxingkz?access_token="+accessToken);
QNetworkRequest request;
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("Content-Type:application/json;charset=UTF-8"));
QJsonObject post_data;;
QJsonDocument document;
post_data.insert("image", imageDatas);
post_data.insert("image_type", "BASE64");
document.setObject(post_data);
QByteArray post_param = document.toJson(QJsonDocument::Compact);
to_recoginze = new QNetworkAccessManager(this);
connect(to_recoginze, SIGNAL(finished(QNetworkReply*)), this, SLOT(recognizeResult(QNetworkReply*)));
to_recoginze->post(request, post_param);
}
- 接收数据函数同样需要判断接收数据有没有包含错误,如果没有则按照json格式解析所需要的数据:yes与no的置信度,并打印。
//识别结果接收
void MainWindow::recognizeResult(QNetworkReply* pReply){
QByteArray replaystring = pReply->readAll();
qDebug()<<"reganizeResult()"+replaystring;
QNetworkReply::NetworkError err = pReply->error();
if(err != QNetworkReply::NoError) {
qDebug() << "Failed: " << replaystring;
}else {
//获取内容
QJsonParseError jsonErr;
QJsonDocument jsonDoc = QJsonDocument::fromJson(replaystring, &jsonErr);
//qDebug()<<jsonDoc.object();
double yesNum = 0, noNum = 0;
if (jsonDoc.isObject()){
QJsonObject jsonObj = jsonDoc.object();
QJsonValue results = jsonObj.value("results");
if (results.isArray()) {
QJsonArray resultsArray = results.toArray();
for (int i = 0; i < resultsArray.count(); i++) {
QJsonObject temp = resultsArray[i].toObject();
if(temp.take("name") == "yes")
yesNum = temp.take("score").toDouble();
else
noNum = temp.take("score").toDouble();
}
}
}
qDebug()<<yesNum;
qDebug()<<noNum;
}
}
- 识别结果如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/f8294d521bda251687d1b4c94da30298.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b8ff9dd94225eafe9668864b5a6cb66e.png)
打开图片进行识别
void MainWindow::on_openfile_clicked()
{
QString fileName = QFileDialog::getOpenFileName(
this, tr("open image file"),
"./", tr("Image files(*.bmp *.jpg *.pbm *.pgm *.png *.ppm *.xbm *.xpm);;All files (*.*)"));
if(fileName.isEmpty())
{
QMessageBox mesg;
mesg.warning(this,"警告","打开图片失败!");
return;
}
qDebug()<<fileName;
QImage image(fileName);
ui->imageview->setPixmap(QPixmap::fromImage(image));
//设置request按钮的可点击属性
ui->request->setEnabled(true);
getAccessToken = connect(ui->request, SIGNAL(clicked()), this, SLOT(on_getAccessToken()));
imageToBase64(image);
}
- 识别结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/c0a3c370c6740aee9a4fd6c6fd36acb4.png)
按照惯例——上完整工程代码
- 项目文件目录:
![](https://i-blog.csdnimg.cn/blog_migrate/c285f6fe625681778af1bd9d0e08c7ef.png)
- Camera.pro
QT += core gui multimedia multimediawidgets
QT += network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
- mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QCamera>
#include <QCameraViewfinder>
#include <QCameraImageCapture>
#include <QComboBox>
#include <QNetworkAccessManager>
#include <QNetworkReply>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void setfblComobox(QCamera *camera);
void imageToBase64(QImage image);
private slots:
void on_screenshot_clicked();
void on_cameraType_activated(int index);
void on_imageCaptured(int id, const QImage &preview);
void on_fbl_activated(int index);
void on_openfile_clicked();
//获取AccessToken
void on_getAccessToken();
//识别
void on_recognize();
//accessToken结果接收
void accessTokenResult(QNetworkReply* pReply);
//识别结果接收
void recognizeResult(QNetworkReply* pReply);
private:
Ui::MainWindow *ui;
QCamera *camera;
QCameraViewfinder *viewfind;
QList<QCameraInfo> cameras;
QCameraImageCapture *imageCapture;
QList<QSize> mResSize = {};//分辨率List 定义
QComboBox *box;
QMetaObject::Connection getAccessToken;
QNetworkAccessManager *get_accessToken;
QNetworkAccessManager *to_recoginze;
QString accessToken, imageDatas;
};
#endif // MAINWINDOW_H
- main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
- mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QCameraInfo>
#include <QJsonParseError>
#include <QJsonObject>
#include <QBuffer>
#include <QJsonArray>
#include <QFileDialog>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QComboBox *cameraType = new QComboBox();
box = ui->fbl;
cameraType = ui->cameraType;
cameraType->clear();
cameras = QCameraInfo::availableCameras();
foreach(const QCameraInfo &cameraInfo, cameras) {
//qDebug() << "CameraInfo:" << cameraInfo;
cameraType->addItem(cameraInfo.description());
}
camera = new QCamera(this);
viewfind = new QCameraViewfinder();
viewfind->show();
camera->setViewfinder(viewfind);
imageCapture = new QCameraImageCapture(camera);
camera->start();
connect(imageCapture, SIGNAL(imageCaptured(int, QImage)), this, SLOT(on_imageCaptured(int, QImage)));
setfblComobox(camera);
//设置request按钮的文本显示
ui->request->setText("获取AccessToken");
ui->request->setDisabled(true);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_cameraType_activated(int index)
{
index = ui->cameraType->currentIndex();
qDebug()<<"Index"<< index <<": "<< ui->cameraType->currentText();
camera->stop();
camera = new QCamera(cameras[index]);
camera->setCaptureMode(QCamera::CaptureVideo);
camera->setViewfinder(viewfind);
imageCapture = new QCameraImageCapture(camera);
camera->start();
connect(imageCapture, SIGNAL(imageCaptured(int, QImage)), this, SLOT(on_imageCaptured(int, QImage)));
setfblComobox(camera);
}
void MainWindow::on_imageCaptured(int id, const QImage &preview){
Q_UNUSED(id);
int with = ui->imageview->width();
int height = ui->imageview->height();
QPixmap pixmap = QPixmap::fromImage(preview);
//QPixmap fitpixmap = pixmap.scaled(with, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); // 饱满填充
QPixmap fitpixmap = pixmap.scaled(with, height, Qt::KeepAspectRatio, Qt::SmoothTransformation); // 按比例缩放
ui->imageview->setPixmap(fitpixmap);
if(ui->request->text() != "识别"){
//设置request按钮的可点击属性
ui->request->setEnabled(true);
getAccessToken = connect(ui->request, SIGNAL(clicked()), this, SLOT(on_getAccessToken()));
}
imageToBase64(preview);
}
void MainWindow::on_screenshot_clicked()
{
qDebug()<<"ScreenShot";
imageCapture->capture();
}
void MainWindow::on_fbl_activated(int index){
index = ui->fbl->currentIndex();
//qDebug()<<"Index"<< index <<": "<< ui->fbl->currentText();
//qDebug()<<"mResSize:"<<mResSize[index];
//设置摄像头参数
QCameraViewfinderSettings set;
set.setResolution(mResSize[index]);
camera->setViewfinderSettings(set);
}
void MainWindow::setfblComobox(QCamera *camera){
mResSize.clear();
mResSize = camera->supportedViewfinderResolutions();
box->clear();
int i=0;
foreach (QSize msize, mResSize) {
//qDebug()<<msize;
box->addItem(QString::number(msize.width(),10)+"*"+QString::number(msize.height(),10), i++);
} //摄像头支持分辨率打印
box->setCurrentIndex(i-1);
}
void MainWindow::on_openfile_clicked()
{
QString fileName = QFileDialog::getOpenFileName(
this, tr("open image file"),
"./", tr("Image files(*.bmp *.jpg *.pbm *.pgm *.png *.ppm *.xbm *.xpm);;All files (*.*)"));
if(fileName.isEmpty())
{
QMessageBox mesg;
mesg.warning(this,"警告","打开图片失败!");
return;
}
qDebug()<<fileName;
QImage image(fileName);
//等比缩放显示图片
int with = ui->imageview->width();
int height = ui->imageview->height();
QPixmap pixmap = QPixmap::fromImage(image);
//QPixmap fitpixmap = pixmap.scaled(with, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); // 饱满填充
QPixmap fitpixmap = pixmap.scaled(with, height, Qt::KeepAspectRatio, Qt::SmoothTransformation); // 按比例缩放
ui->imageview->setPixmap(fitpixmap);
//设置request按钮的可点击属性
ui->request->setEnabled(true);
getAccessToken = connect(ui->request, SIGNAL(clicked()), this, SLOT(on_getAccessToken()));
imageToBase64(image);
}
//获取AccessToken
void MainWindow::on_getAccessToken(){
QString apiKey = "x7AfM65W69R5Z6fY8wVYvOct";//替换为个人的apiKey
QString secretKey = "qw1eG**************AV6d7oNM";//替换为个人的secretKey
QNetworkRequest request;
request.setRawHeader("Content-Type", "application/json");
request.setRawHeader("charset", "UTF-8");
QUrl url("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=" + apiKey + "&client_secret=" + secretKey);
request.setUrl(url);
QByteArray data;
get_accessToken = new QNetworkAccessManager(this);
connect(get_accessToken, SIGNAL(finished(QNetworkReply*)), this, SLOT(accessTokenResult(QNetworkReply*)));
get_accessToken->post(request, data);
}
//识别
void MainWindow::on_recognize(){
if (imageDatas.isEmpty())
{
qDebug() << "image base64 empty";
return;
}
if (accessToken.isEmpty())
{
qDebug() << "accessToken empty";
return;
}
QUrl url;
url.setUrl("https://aip.baidubce.com/rpc/2.0/ai_custom/v1/classification/moxingkz?access_token="+accessToken);
QNetworkRequest request;
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("Content-Type:application/json;charset=UTF-8"));
QJsonObject post_data;;
QJsonDocument document;
post_data.insert("image", imageDatas);
post_data.insert("image_type", "BASE64");
document.setObject(post_data);
QByteArray post_param = document.toJson(QJsonDocument::Compact);
to_recoginze = new QNetworkAccessManager(this);
connect(to_recoginze, SIGNAL(finished(QNetworkReply*)), this, SLOT(recognizeResult(QNetworkReply*)));
to_recoginze->post(request, post_param);
}
//accessToken结果接收
void MainWindow::accessTokenResult(QNetworkReply* pReply){
if(pReply->error() == QNetworkReply::NoError){
QByteArray string = pReply->readAll();
//获取内容
QJsonParseError jsonErr;
QJsonDocument jsonDoc = QJsonDocument::fromJson(string, &jsonErr);
if (jsonDoc.isObject()){
QJsonObject jsonObj = jsonDoc.object();
accessToken = jsonObj.take("access_token").toString();
qDebug()<<"access_token:"+accessToken;
//替换request按钮功能
disconnect(getAccessToken);
ui->request->setText("识别");
connect(ui->request, SIGNAL(clicked()), this, SLOT(on_recognize()));
}
}
}
//识别结果接收
void MainWindow::recognizeResult(QNetworkReply* pReply){
QByteArray replaystring = pReply->readAll();
qDebug()<<"reganizeResult()"+replaystring;
QNetworkReply::NetworkError err = pReply->error();
if(err != QNetworkReply::NoError) {
qDebug() << "Failed: " << replaystring;
}else {
//获取内容
QJsonParseError jsonErr;
QJsonDocument jsonDoc = QJsonDocument::fromJson(replaystring, &jsonErr);
//qDebug()<<jsonDoc.object();
double yesNum = 0, noNum = 0;
if (jsonDoc.isObject()){
QJsonObject jsonObj = jsonDoc.object();
QJsonValue results = jsonObj.value("results");
if (results.isArray()) {
QJsonArray resultsArray = results.toArray();
for (int i = 0; i < resultsArray.count(); i++) {
QJsonObject temp = resultsArray[i].toObject();
if(temp.take("name") == "yes")
yesNum = temp.take("score").toDouble();
else
noNum = temp.take("score").toDouble();
}
}
}
qDebug()<<yesNum;
qDebug()<<noNum;
}
}
void MainWindow::imageToBase64(QImage image){
//image to base64
QByteArray imageData;
QBuffer buff(&imageData);
image.save(&buff, "jpg");
imageDatas = imageData.toBase64();
}
- mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>824</width>
<height>549</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<property name="font">
<font>
<pointsize>7</pointsize>
</font>
</property>
<layout class="QHBoxLayout" stretch="4,2">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="imageview">
<property name="maximumSize">
<size>
<width>680</width>
<height>400</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_2" native="true">
<widget class="QWidget" name="widget_3" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>100</y>
<width>261</width>
<height>161</height>
</rect>
</property>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>0</x>
<y>40</y>
<width>61</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>摄像头</string>
</property>
</widget>
<widget class="QComboBox" name="cameraType">
<property name="geometry">
<rect>
<x>70</x>
<y>50</y>
<width>181</width>
<height>23</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>0</x>
<y>110</y>
<width>71</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>分辨率</string>
</property>
</widget>
<widget class="QComboBox" name="fbl">
<property name="geometry">
<rect>
<x>70</x>
<y>110</y>
<width>181</width>
<height>23</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
</widget>
</widget>
<widget class="QWidget" name="widget_4" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>320</y>
<width>261</width>
<height>181</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="screenshot">
<property name="text">
<string>截图</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="openfile">
<property name="text">
<string>打开图片</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="request">
<property name="text">
<string>识别</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
![](https://i-blog.csdnimg.cn/blog_migrate/55dc029358f8ba7461407fa9924eab04.png)
- 以上代码个人通过多方渠道搜集学习得来,不免有些问题,如有问题请留言交流~