QT基于百度的人脸考勤系统

目录

一、百度智能云介绍

二、代码 

1、camera类

2、widget类 

3、register类

4、dialog类

三、效果演示

四、改进

获取完整代码请前往:GitHub - zhaohigh/Qt-chatrobut


目前还在学习中,需要练练手,就写了一个基于百度人脸识别的考勤系统。

百度人脸识别的功能很强大,也很完善了,所以就直接调用百度的人脸识别API,自己再QT完善一下界面就好啦。

一、百度智能云介绍

        需要在百度智能云里领取人脸识别的权限和查看调用的接口信息。下面是百度智能云的连接:

百度智能云:百度智能云-云智一体深入产业

        1、未注册的用户需要先注册才能使用,它是需要实名注册的。

        2、登录进去需要领取人脸识别的权限:

         领取的时候有许多选择,看需要哪些就可以领取使用。注意这个是有免费次数的用完了就需要付费使用了。

        4、之后就是创建应用列表了,它会给API Key和Secret Key,这个就是获取Access_token来调用百度服务的,直接可以复制使用的。

        5、然后可以在技术文档或者API调试里查看需要使用的AI服务的请求URL了。

 

 

二、代码 

1、开始之前需要在.pro文件中添加多媒体和网络的类

 

 2、因为我设计了几个界面,所以创建了几个类:

1、camera类

首先创建了一个camera的类,用来获取摄像头信息,并显示在设置好的label控件上。

camera.文件需要添加一些多媒体类的头文件,并定义相应的指针。

 在构造函数中创建相应的对象。

Camera::Camera(QWidget *parent) : QWidget(parent)
{
        //创建摄像头对象
       camera = new QCamera(this);
       //创建摄像头视图对象(父对象为Widget容器)
       viewfinder = new QCameraViewfinder(this);
        //设置图像捕获内容
       imagecapture = new QCameraImageCapture(camera);
}

 并设置一些槽函数,完成相应的功能。

//设置将摄像头内容添加到label标签上的槽函数
void Camera::setLabel(QLabel *label)
{
    //设置于与label标签一样大
    viewfinder->resize(label->size());
    //设置摄像头内容到label控件
    viewfinder->setParent(label);
     //自动填充背景框(警告:背景框不能有样式表内容,否则将失效)
//    viewfinder->autoFillBackground();
    viewfinder->show();
}
//打开摄像头的槽函数
void Camera::startCamera()
{
     //设置摄像头试图对象到摄像头
    camera->setViewfinder(viewfinder);
    camera->setCaptureMode(QCamera::CaptureStillImage);
    imagecapture->capture();
    camera->start();
}
//关闭摄像头的槽函数
void Camera::stopCamera()
{
    camera->stop();
//    camera->setViewfinder(nullptr);
}
//设置能够截屏的槽函数
QImage Camera::screenShort()
{
    // 获取摄像头当前帧
    QImage image = viewfinder->grab().toImage();
    if(image.isNull()){
        QMessageBox::warning(this,"警告","请重新识别或截图","确认");
    }
    return image;
}

2、widget类 

在widget.h文件中需要添加camera的头文件,并定义。

在构造函数中获取Access_token,使得每次打开就能拉取新的Access_token(因为百度的Access_token的期限只有30天)。

#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setWindowTitle("人脸识别考勤");
    this->setWindowIcon(QIcon(":/img/space.jpg"));
    ui->label->setFixedSize(ui->widget->size());
    //设置textEdit能够每次换行
    ui->textEdit->insertPlainText("");
    //设置按钮样式
   camera.setBtnStyle(ui->faceBtn);
   camera.setBtnStyle(ui->addUserBtn);
   camera.setBtnStyle(ui->quitBtn);

    //创建设置摄像头
    camera.setLabel(ui->label);
    //打开摄像头
    camera.startCamera();


    //获取百度人脸识别access_token
    manager = new QNetworkAccessManager;
    QByteArray url =QString("client_id=%1&client_secret=%2&grant_type=client_credentials")
            .arg(client_id)//就是API Key,就直接复制过来就好啦
            .arg(client_secret).toLatin1();    //这个是Secret Key
    QNetworkRequest request;
    request.setUrl(QUrl(baiduUrl+url));
    connect(manager,&QNetworkAccessManager::finished,this,&Widget::Accesstokenreply);
    manager->get(request);


}
/*获取Access_token,需要解析json格式*/
void Widget::Accesstokenreply(QNetworkReply *reply)
{
    QByteArray byte = reply->readAll();
    QByteArray bytearry= QString(byte).toUtf8();
    QJsonDocument document= QJsonDocument::fromJson(bytearry);
    if(document.isObject()){
        QJsonObject json = document.object();
        if(json.contains("access_token")){
            QJsonValue jsonval = json.value("access_token");
            access_token = jsonval.toString();
            //             qDebug()<<access_token;
        }
    }
}

到这里就可以利用Access_token直接调用人脸识别功能的一些URL使用啦。

我这里调用了人脸检测和人脸库的人脸注册(在第二个界面用来注册人脸)及人脸检索三个功能。

void Widget::Baiduface()
{
    //发送图片给百度人脸检测,获取json信息
    http = new QNetworkAccessManager;
    QString Url= "https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=";
    QNetworkRequest Request;
    Request.setUrl(QUrl(Url+access_token));
    //设置头部
    Request.setRawHeader("Content-Type", "application/json");
    //请求body参数(有一些非必选项看自己需要是否选择,看以查看百度的技术文档)
    QJsonObject json;
    json["image"]=ima;
    json["image_type"]="BASE64";
//这里可以自己选择需要哪些内容,就请求哪些
    json["face_field"]="age,beauty,expression,face_shape,gender,glasses,emotion";
    json["max_face_num"]=2;
    json["face_type"]="LIVE";
    QByteArray data = QJsonDocument(json).toJson();
    connect(http,&QNetworkAccessManager::finished,this,&Widget::RebackFacemessge);
    http->post(Request,data);

    //百度人脸库内检索,看是否存在
    faceSearch = new QNetworkAccessManager;
    QString url = "https://aip.baidubce.com/rest/2.0/face/v3/search?access_token=";
    QNetworkRequest request;
    request.setUrl(QUrl(url+access_token));
    //设置头部
    request.setRawHeader("Content-Type", "application/json");
    //请求body参数(同样有非必选项)
    QJsonObject json2;
    json2["image"] = ima;
    json2["image_type"] = "BASE64";
    json2["group_id_list"] = "这是自己创建的组ID";
    json2["quality_control"] = "NORMAL";
    json2["match_threshold"] = 80;
    QByteArray data2 = QJsonDocument(json2).toJson();
    connect(faceSearch,&QNetworkAccessManager::finished,this,&Widget::RebackFaceSearch);
    faceSearch->post(request,data2);
}

上面的ima是图片转成base64格式的,因为百度的图片信息是string类型的,所以需要自己声明定义一个槽函数用来将截取的人脸图片转成base64格式。

QString Widget::ImageToString(const QImage &image)
{
    // 将 QImage 转换为 QString
    QByteArray byteArray;
    QBuffer buffer(&byteArray);
    buffer.open(QIODevice::WriteOnly);
    image.save(&buffer, "PNG");  // 保存为 PNG 格式的字节数组
    QString imageString = QString(byteArray.toBase64());
   // qDebug()<<"imagestring:"<<imageString;
    buffer.close();
    return imageString;
}

获取的json信息,根据自己需要解析内容并打印出来。

/*获取百度人脸检测结果*/
void Widget::RebackFacemessge(QNetworkReply *reply)
{
    QByteArray byte = reply->readAll();
    QByteArray bytearry = QString(byte).toUtf8();
    QJsonDocument docunment = QJsonDocument::fromJson(bytearry);
    if(docunment.isObject()){
        QJsonObject json = docunment.object();
        //可以先打印出json信息,再根据json内的信息进行解析
        //        qDebug()<<"json:"<<json;
        if(json.contains("result")){
            QJsonObject obj = json.take("result").toObject();
            if(obj.contains("face_list")){
                //                qDebug()<<"obj:"<<obj;
                QJsonArray array = obj.value("face_list").toArray();
                //                qDebug()<<"array"<<array;
                //face_list是一个数组,需要循环遍历才能解析除所有内容,下面的中文可以根据自己的想法进行修改,应该很有趣
                for(int i=0;i<array.size();i++){
                    QJsonObject obj1 = array.at(i).toObject();
                    if(obj1.contains("age")){
                        double age = obj1.value("age").toDouble();
                        ui->textEdit->append(QString("年龄:%1").arg(age));
                    }
                    if(obj1.contains("gender")){
                        QJsonObject obj2 = obj1.value("gender").toObject();
                        if(obj2.contains("type")){
                            QString type = obj2.value("type").toString();
                            if(type == "male"){
                                ui->textEdit->append("性别:男");
                            }else{
                                ui->textEdit->append("性别:女");
                            }
                        }
                    }
                    if(obj1.contains("beauty")){
                        double beauty = obj1.value("beauty").toDouble();
                        ui->textEdit->append(QString("颜值得分:%1").arg(beauty));
                    }
                    if(obj1.contains("emotion")){
                        QJsonObject obj2 = obj1.value("emotion").toObject();
                        if(obj2.contains("type")){
                            QString type = obj2.value("type").toString();
                            if(type == "sad"){
                                ui->textEdit->append("情绪:伤心");
                            }else if(type == "happy"){
                                ui->textEdit->append("情绪:高兴");
                            }else if(type == "neutral"){
                                ui->textEdit->append("情绪:不带感情的");
                            }else if(type == "angry"){
                                ui->textEdit->append("情绪:愤怒");
                            }else if(type == "disgust"){
                                ui->textEdit->append("情绪:厌恶");
                            }else if(type == "fear"){
                                ui->textEdit->append("情绪:恐惧");
                            }else if(type == "surprise"){
                                ui->textEdit->append("情绪:惊讶");
                            }else if(type == "pouty"){
                                ui->textEdit->append("情绪:撅嘴");
                            }else if(type == "grimace"){
                                ui->textEdit->append("情绪:鬼脸");
                            }
                        }
                    }
                    if(obj1.contains("glasses")){
                        QJsonObject obj2 = obj1.value("glasses").toObject();
                        if(obj2.contains("type")){
                            QString type = obj2.value("type").toString();
                            if(type == "none"){
                                ui->textEdit->append("没戴眼镜");
                            }else {
                                ui->textEdit->append("戴了眼镜");
                            }
                        }
                    }
                    if(obj1.contains("expression")){
                        QJsonObject obj2 = obj1.value("expression").toObject();
                        if(obj2.contains("type")){
                            QString type = obj2.value("type").toString();
                            if(type == "none"){
                                ui->textEdit->append("表情:无表情");
                            }else if(type == "smile"){
                                ui->textEdit->append("表情:微笑");
                            }else if(type == "laugh"){
                                ui->textEdit->append("表情:大笑");
                            }
                        }
                    }
                    if(obj1.contains("face_shape")){
                        QJsonObject obj2 = obj1.value("face_shape").toObject();
                        if(obj2.contains("type")){
                            QString type = obj2.value("type").toString();
                            if(type == "oval"){
                                ui->textEdit->append("脸型:椭圆脸");
                            }else if(type == "square"){
                                ui->textEdit->append("脸型:方形脸");
                            }else if(type == "triangle"){
                                ui->textEdit->append("脸型:尖脸");
                            }else if(type == "heart"){
                                ui->textEdit->append("脸型:鹅蛋脸");
                            }else if(type == "round"){
                                ui->textEdit->append("脸型:圆脸");
                            }
                        }
                    }
                }
            }
        }
    }
}
/*获取百度人脸库检索结果*/
void Widget::RebackFaceSearch(QNetworkReply *reply)
{
    QByteArray byte = reply->readAll();
    QByteArray bytearry = QString(byte).toUtf8();
    QJsonDocument docunment = QJsonDocument::fromJson(bytearry);
    if(docunment.isObject()){
        QJsonObject json = docunment.object();
        qDebug()<<"json:"<<json;
        if(json.contains("error_msg")){
        //这里是自己想要多的一些错误提示,所以才判断了很多,其实可以只判断成功与否
            QString data = json.value("error_msg").toString();
            if(data == "Open api daily request limit reached"){
                QMessageBox::critical(this,"错误","今日识别次数已用完","确认");
                flag=false;
            }else if(data =="Open api request limit reached"){
                QMessageBox::critical(this,"错误","请求速率过快,请重新识别","确认");
                flag=false;
            }/*else if(data =="Open api qps request limit reached"){
                QMessageBox::critical(this,"错误","请求速率过快,请重新识别","确认");
                 flag=false;
            }*/else if(data =="Open api total request limit reached"){
                QMessageBox::critical(this,"错误","免费次数已用完,请付费开通","确认");
                flag=false;
            }else if(data =="Invalid parameter"){
                QMessageBox::critical(this,"错误","无效接口","确认");
                flag=false;
            }else if(data =="match user is not found"){
                QMessageBox::critical(this,"错误","用户未注册,请注册后识别","确认");
                flag=false;
            }
        }
        //设置了一个标签,初始值为true,如果出现错误提示就直接退出,不进行下面的解析了,因为有的错误会出现错误提示的同时,也会有结果返回
        if(flag == false)
            return;
        if(json.contains("result")){
            QJsonObject obj = json.value("result").toObject();
            if(obj.contains("user_list")){
                QJsonArray array = obj.value("user_list").toArray();
                for(int i=0;i<array.size();i++){
                    QJsonObject obj2 = array.at(i).toObject();
                    if(obj2.contains("score")){
                        double score = obj2.value("score").toDouble();
                        if(score<80){
                            qDebug()<<"识别失败";
                            dialog.trueOrfalse("识别失败");
                            return;
                        }else {
                            qDebug()<<"识别成功";
                            dialog.trueOrfalse("识别成功");
                        }
                    }
                    if(obj2.contains("user_id")){
                        QString user = obj2.value("user_id").toString();
                        QStringList parts = user.split("_");
                        //qDebug()<<"用户名:"<<parts.at(0);
                        //qDebug()<<"工号:"<<parts.at(1);
                        //dialog是自己封装的弹出显示结果的类
                        dialog.setIoformation(QString("用户名:%1").arg(parts.at(0)));
                        dialog.setIoformation(QString("工号:%1").arg(parts.at(1)));
                        dialog.show();
                    }
                }
            }
        }
    }
}

这是一个截图按钮的控件。

/*截图*/
void Widget::on_faceBtn_clicked()
{
    
    //清除image
    image.fill(Qt::white);
    // 调用函数获取摄像头当前帧
    image = camera.screenShort();
    //iamge转换成string类型
    ima = ImageToString(image);
    //          qDebug()<<"ima"<<ima;
    ui->label_image->clear();
    //上传的截图放在label标签上
    QPixmap pixmap = QPixmap::fromImage(image);
    ui->label_image->setPixmap(pixmap);
    ui->label_image->setScaledContents(true);

}

3、register类

这是第二个界面用来添加用户注册的功能。

在构造函数中,传入了一个Qstring类型的变量,用来传入最开始获取的Access_token就可以不用再次拉取了。

#include "register.h"
#include "ui_register.h"
#include<QDebug>
Register::Register(QString access_token, QWidget* parent ) :
    QWidget(parent),
    ui(new Ui::Register)
{
    ui->setupUi(this);
    this->setWindowTitle("添加人脸识别");
    this->setWindowIcon(QIcon(":/img/space.jpg"));
    token = access_token;
    //创建设置摄像头
    camera.setLabel(ui->label);
    camera.startCamera();
    //设置按钮样式
    camera.setBtnStyle(ui->screenShortBtn);
    camera.setBtnStyle(ui->screenShortBtn_2);
    camera.setBtnStyle(ui->loginBtn);
    camera.setBtnStyle(ui->rebackBtn);

}

这里同样也有截图和image类型的转成base64的转化。

这是注册用户人脸的按钮,也是最后一个请求URL了。

void Register::on_loginBtn_clicked()
{
    //判断用户名和工号是否为空
    if(ui->lineEdit_name->text().isEmpty() || ui->lineEdit_port->text().isEmpty()){
        QMessageBox::warning(this,"警告","请输入姓名或账号","确认");
        return;
    }
    //判断用户名是否字母
    for(const QChar&c:ui->lineEdit_name->text()){
        if(!c.isLetter()){
            QMessageBox::warning(this,"非法操作","用户名格式不正确,请重新输入","确认");
            return;
        }
    }
    //判断工号是否是数字
    bool isNumber=false;
    ui->lineEdit_port->text().toInt(&isNumber);
    if(isNumber == false){
        QMessageBox::warning(this,"非法操作","工号格式不正确,请重新输入","确认");
        return;
    }

    http = new QNetworkAccessManager;
    //百度人脸识别注册url
    QString url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/add?access_token=";
    QNetworkRequest request;
    request.setUrl(QUrl(url+token));
    request.setRawHeader("Content-Type", "application/json");
    QJsonObject json;
    //与前两个一样,自己选择
    json["image"]=imastring;
    json["image_type"]="BASE64";
    json["group_id"]="自己创建的组ID";
    json["user_id"]=QString("%1_%2").arg(ui->lineEdit_name->text()).arg(ui->lineEdit_port->text().toInt());
    json["quality_control"]="NORMAL";
    QByteArray data = QJsonDocument(json).toJson();
    connect(http,&QNetworkAccessManager::finished,this,&Register::RebackFacemessge);
    http->post(request,data);

}

上面注册功能的输入信息自己还加了一些判断,因为百度人脸库注册的user_id要求只能数字、字母和下划线,这里自己就只允许用户名输入字母,工号输入数字,请求时自己在两者之间加一个下滑线,也方便到时候检索时返回结果解析出的user_id根据”_"来分割用户名和工号了。

这里获取的json解析的内容只有一些错误判断了,还是很好操作。

/*注册获取json信息*/
void Register::RebackFacemessge(QNetworkReply *reply)
{
    //获取到信息,就清空用户输入的信息
    ui->lineEdit_name->clear();
    ui->lineEdit_port->clear();

    QByteArray byte = reply->readAll();
    QByteArray bytearry = QString(byte).toUtf8();
    QJsonDocument docunment = QJsonDocument::fromJson(bytearry);
    if(docunment.isObject()){
        QJsonObject json = docunment.object();
        //qDebug()<<"json"<<json;
        if(json.contains("error_msg")){
            QString data = json.value("error_msg").toString();
            if(data == "SUCCESS"){
                QMessageBox::information(this,"注册","恭喜!人脸库添加成功");
                return;
            }else if(data == "param[user_id] format error"){
                QMessageBox::warning(this,"警告","用户名或账号格式不正确","确认");
                return;
            }else if(data == "param[quality_control]format error"){
                QMessageBox::critical(this,"错误","人脸质量过低,请重新注册");
                return;
            }else if(data == "add face fail"){
                QMessageBox::critical(this,"错误","人脸图片添加失败,请重新注册");
                return;
            }else if(data == "user is already exist"){
                QMessageBox::critical(this,"错误","用户已存在,请不要重复注册");
                return;
            }else {
                QMessageBox::critical(this,"错误","人脸库注册失败,请重新注册");
                return;
            }
        }
    }
}

4、dialog类

这里弹出的界面信息就比较简单了,只需要在检索时传入获取的内容并弹出界面就行了。在第一个界面定义,直接调用函数接口就可以了。

void Dialog::on_pushButton_clicked()
{
    this->close();
}

void Dialog::trueOrfalse(const QString data)
{
    if(data == "识别失败"){
        ui->label_pixmap->setPixmap(QPixmap(":/img/fault.jpg"));
        ui->textEdit->setText("识别失败");
        return;
    }else if(data == "识别成功"){
        ui->label_pixmap->setPixmap(QPixmap(":/img/right.jpg"));
        ui->textEdit->append("识别成功");
    }
}

void Dialog::setIoformation(const QString data)
{
    ui->textEdit->append(data);
}

void Dialog::startDialog()
{
    this->show();
}

三、效果演示

 

 

 注:他这个颜值得分算法肯定是不准确的,也还好不是本人亲自测试的(狗头保命)

四、改进

这次时间比较紧,还有许多可以改进的地方:

        1、界面问题可以更美观一点,这个实在是丑得不行。

        2、可以去掉手动截图发送识别的功能,可以增加活体检测,并自动截图识别返回信息。

        3、可以增加语音功能,将识别成功与否的结果说出来。

获取完整代码请前往:GitHub - zhaohigh/Qt-chatrobut

  • 9
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值