基于QT的人脸识别考勤管理系统【二】

前言:

上一篇我们实现了考勤管理系统的用户考勤打卡系统https://blog.csdn.net/qq_42449351/article/details/99716413,这一篇我将为大家带来这个系统的第二部分管理员管理系统。

 管理员管理系统实现了管理员的登录界面、用户信息管理、管理员信息管理、当日考勤统计以及通知栏信息的管理,下面我就为大家依次实现这些功能。

一、工程目录解析

同样也要添加opencv中的face模块,只是我没有标注出来

二、配置文件的更改

QT       += core gui sql charts

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = AdminFace
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as 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 you use 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

CONFIG += c++11

SOURCES += \
        main.cpp \
        admin.cpp \
    src/bif.cpp \
    src/eigen_faces.cpp \
    src/face_alignment.cpp \
    src/face_basic.cpp \
    src/facemark.cpp \
    src/facemarkAAM.cpp \
    src/facemarkLBF.cpp \
    src/facerec.cpp \
    src/fisher_faces.cpp \
    src/getlandmarks.cpp \
    src/lbph_faces.cpp \
    src/mace.cpp \
    src/predict_collector.cpp \
    src/regtree.cpp \
    src/trainFacemark.cpp \
    menu.cpp \
    trainthread.cpp \
    maininterface.cpp \
    record.cpp \
    notice.cpp \
    showrecordform.cpp \
    adminmenu.cpp

HEADERS += \
        admin.h \
    face/bif.hpp \
    face/face_alignment.hpp \
    face/facemark.hpp \
    face/facemark_train.hpp \
    face/facemarkAAM.hpp \
    face/facemarkLBF.hpp \
    face/facerec.hpp \
    face/mace.hpp \
    face/predict_collector.hpp \
    src/face_alignmentimpl.hpp \
    src/face_utils.hpp \
    src/precomp.hpp \
    face.hpp \
    menu.h \
    trainthread.h \
    maininterface.h \
    record.h \
    notice.h \
    showrecordform.h \
    adminmenu.h

FORMS += \
        admin.ui \
    menu.ui \
    maininterface.ui \
    record.ui \
    notice.ui \
    showrecordform.ui \
    adminmenu.ui
INCLUDEPATH+=D:\opencv64\include\

             D:\opencv64\include\opencv\

             D:\opencv64\include\opencv2

LIBS += D:\opencv64\x64\mingw\lib\libopencv_*
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

RESOURCES += \
    pic.qrc

三、界面设计

1、登录界面设计

2、主界面设计

3、学生信息管理界面设计

4、管理员信息管理界面

5、当日考勤统计界面

6、通知栏信息的管理界面

四、功能设计

1、登录功能的设计

   该登录采用了双重密码保护,先输入密码,输入完成后在管理员信息表中查询密码是否正确,如果密码正确的话,打开摄像头,开启定时器,每隔20ms去运行绑定的函数,对人脸进行识别,如果识别的是管理员的标号,那么就登陆成功,否则登录失败。密码正确后会进入主界面

 

流程图

重要代码如下: 

void Admin::button_clicked()
{
    //获取信号发送者
    QPushButton *button = dynamic_cast<QPushButton*>( sender());
    QString ch = button->text();

    QString press = ui->pressedit->text();
    if(ch != "<" && ch != "确定")//判断是否点击删除按钮
    {

        press.append(ch);//不是就把ch追加到密码后面
    }
    else if(ch == "<")
    {//是点击删除按钮
        press.remove(press.size()-1, 1);//删除最后一个字符

    }
    else if(ch =="确定")
    {
        pressword =ui->pressedit->text();
        qDebug()<<pressword;
        pass(pressword);

    }
    //设置显示
    ui->pressedit->setText(press);

}
//验证输入密码是否正确
void Admin::pass(QString pressword)
{

    QString sql = QString("select * from Admin where press='%1'").arg(pressword);
    QSqlQuery query(sql);
    while(query.next())
    {
        qDebug()<<query.value(0).toInt()<<query.value(1).toString()<<query.value(2).toString();
        qDebug()<<"密码正确";

        //密码正确使用人脸识别来登录管理员系统
        capture.open(0);
        //训练好的文件名称,放置在可执行文件同目录下
        cascade.load("haarcascade_frontalface_alt2.xml");
        model = face::FisherFaceRecognizer::create();
        //1.加载训练好的分类器
        model->read("F:\\video\\AdminFace\\MyFaceFisherModel.xml");

        mtimer.start(20);

        return ;
    }
    QMessageBox::information(this,"提示","密码错误");
}

2、主菜单的设计

     通过主菜单进入用户信息管理、管理员信息管理、当日考勤统计以及通知栏信息的管理。并通过从登陆界面传过来的管理员信息,来判断该管理员是否有操作管理员信息模块的权限。

#include "maininterface.h"
#include "ui_maininterface.h"
#include "menu.h"
#include "record.h"
#include "notice.h"
#include "adminmenu.h"
#include <QMessageBox>
#include <QSqlQuery>
#include "admin.h"
MainInterface::MainInterface(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainInterface)
{
    ui->setupUi(this);

    //添加数据库驱动
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    //设置数据库名
    db.setDatabaseName("F:\\video\\stuface.db");
    //打开数据库
    if(!db.open())
    {
        qDebug()<<"open error";
    }

}

MainInterface::~MainInterface()
{
    delete ui;

}

//设置是哪个管理员登录进来
void MainInterface::setAdmin(QString name)
{
    this->Admin_name = name;
}


//学生信息管理
void MainInterface::on_stubt_clicked()
{
    Menu *w = new Menu();
    w->show();
    this->close();
}

//考勤统计
void MainInterface::on_recordbt_clicked()
{
    Record *w = new Record();
    w->show();
    this->close();
}
//写通知栏
void MainInterface::on_noticebt_clicked()
{
    Notice *w =new Notice();
    w->show();
    this->close();
}

//管理员信息管理
void MainInterface::on_edmitbt_clicked()
{
    int level = 0;
    qDebug()<<Admin_name;
    //判断管理员权限
    QString sql_select = QString("select level from Admin where name='%1'").arg(Admin_name);
    qDebug()<<sql_select;
    try {
        QSqlQuery query_select(sql_select);
        while(query_select.next())
        {
            level = query_select.value(0).toInt();

        }
    } catch (Exception e) {
        QMessageBox::about(this,"提示","查询数据库失败!");
        return ;
    }

    qDebug()<<level;
    if(level<5)
    {
        QMessageBox::about(this,"提示","您没有权限操作管理员信息");
        return ;
    }

    Adminmenu *w = new Adminmenu();
    w->show();
    this->close();

}
//返回登录界面
void MainInterface::on_backbt_clicked()
{
    Admin *w = new Admin();
    w->show();
    this->close();
}

3、学生信息管理设计

该模块的功能是,输入用户的信息后,采集用户头像,并对采集的头像进行预处理,预处理完成后将图片保存在指定的路径上(学号_姓名),保存图片后通过进程调用python程序,生成训练文件,文件里包含两个内容,一是每一张图片的路径,二是每一个人脸对应的标签,生成训练文件后,就可以通过训练文件一行一行的通过路径找到图片,通过基于Fisher变换的人脸识别的算法对图片进行训练,训练完成后,将用户保存在用户信息表中,也可通过学号,姓名来删除和查找该用户的信息。

采集头像,对图片进行预处理以及训练文件的生成和通过文件进行训练在我的博客 ——基于Opencv3和QT实现人脸识别有详细讲解 https://blog.csdn.net/qq_42449351/article/details/99052241,这里我会着重讲如何在指定的位置保存图片对数据库的操作

打开摄像头的条件是必须输入用户的信息,打开摄像头后会根据学号和姓名生成目录,如果目录存在则不创建。

void Menu::on_openbt_clicked()
{
    QString dir_str = "F:\\video\\AdminFace\\att_faces\\";
    if(!ui->namedit->text().isEmpty())
    {
        QDir dir;
        //判断目录是否存在
        if(!dir.exists(dir_str+ui->namedit->text()+"_"+ui->noedit->text()))
        {
            bool res = dir.mkpath(dir_str+ui->namedit->text()+"_"+ui->noedit->text());
            qDebug()<<"新建目录是否成功"<<res;
        }
    }
    else {
        QMessageBox::about(this,"提示","请输入学生姓名");
        return ;
    }
    cap.open(0);

    mtimer.start(20);
}

通过opencv官方训练好的 人脸检测分类器(haarcascade_frontalface_alt2.xml)来判断现在摄像头里有几个人脸,如果只有一个人脸时,才能进行拍照,然后用imwrite函数,将图片保存到指定路径,在这里要注意:imwrite只支持string类型的,所以要将QString转为string,在转的过程中,先将QString转为QByteArray,再转为string型,这样中文路径也能支持。

//拍照
void Menu::on_collectbt_clicked()
{
    //当只有一个人脸的时候才进行采集
    if (faces.size() == 1)
    {
        Mat faceROI = frame_gray(faces[0]);//在灰度图中将圈出的脸所在区域裁剪出
        //cout << faces[0].x << endl;//测试下face[0].x
        ::resize(faceROI, myFace, Size(92, 112));//将兴趣域size为92*112
        putText(frame, to_string(pic_num), faces[0].tl(), 3, 1.2, (0, 0, 225), 2, 0);//在 faces[0].tl()的左上角上面写序号
        cvtColor(frame,frame,CV_RGB2GRAY);
        QImage image(frame.data, frame.cols, frame.rows, frame.step1() , QImage::Format_Grayscale8);
        QPixmap mmp = QPixmap::fromImage(image);
        ui->label_7->setScaledContents(true);
        ui->label_7->setPixmap(mmp);
        ui->label_7->show();
        //将图片保存的路径为  学号_姓名
        QString dir = "F:\\video\\AdminFace\\att_faces\\"+ui->namedit->text()+"_"+ui->noedit->text()+"\\"+QString::number(pic_num)+".jpg";

        //先进行转码在转字符串
        string filename = dir.toLocal8Bit().toStdString();

        imwrite(filename, myFace);//存在dir路径下

        imshow("src", myFace);//显示下size后的脸

        ui->label_8->setText(QString::number(pic_num)+"张");
        pic_num++;

        if(waitKey(10000) == 'q')
        {
            destroyWindow("src");//:销毁指定的窗口

        }

    }
}

   对sqlite3的操作基本上就是下面几行代码,打包好sql语句,然后去运行sql语句

 //判断信息是否存在,存在则不创建
    QString sql_select = QString("select * from Stu where Sno='%1'").arg(ui->namedit->text());
    QSqlQuery query_select(sql_select);
    while(query_select.next())
    {
        QMessageBox::about(this,"提示","学生信息已存在,添加失败");
        qDebug()<<"信息已存在,添加失败";
        return ;
    }

4、管理员信息管理

在主菜单已经判断只有权限高的管理员(超级管理员)才能进入这个模块,这个模块是给超级管理员对管理员进行管理,显示、删除和将用户提升为管理员的操作,这里面的做法还是对数据库进行操作,主要是操作用户信息表和管理员信息表。

使用QSqlTableModel数据模型,将模型绑定管理员信息表(Admin),与tableview组件也进行绑定,查询的数据显示在该组件上

  //创建数据模型
  model = new QSqlTableModel();
  model->select();
  model->setTable("Admin");//给模型绑定数据表格
  ui->tableView->setModel(model);//把模型与显示view绑定
  model->select();//查询所有的数据

删除某一个管理员

//删除管理员信息
void Adminmenu::on_deletebt_clicked()
{
    if(ui->titleclearedit ->text() == nullptr)
    {
        QMessageBox::about(this,"提示","请输入姓名再删除!");
        return ;
    }
   //判断信息是否存在,不存在则不进行删除
   QString sql_select = QString("select * from Admin where name='%1'").arg(ui->titleclearedit->text());
   QSqlQuery query_select(sql_select);
   while(query_select.next())
   {
       QString sql = QString("delete from Admin where name='%1'").arg(ui->titleclearedit->text());
       QSqlQuery query_select(sql);

       QMessageBox::about(this,"提示","成功删除该类通知");

       model->select();//查询所有的数据
       return ;
   }
   QMessageBox::about(this,"提示","无该管理员信息!");

}

将用户提升为管理员

//设置为管理员
void Adminmenu::on_setbt_clicked()
{
    if(flag == 1)
    {
        flag =0;
        QMessageBox::about(this,"提示","请选择升级谁为管理员!");
        return ;
    }
    //qDebug
   //判断信息是否存在,存在则不创建
   QString sql_select = QString("select * from Admin where name='%1'").arg(this->name);
   QSqlQuery query_select(sql_select);
   while(query_select.next())
   {
       QMessageBox::about(this,"提示","该管理员已存在,无需添加");
       return ;
   }

   //将用户信息添加到管理员信息表中
   QString sql = QString("insert into Admin(name,press,level,label) values('%1','123456',1,%2)").arg(this->name).arg(this->Sno);
    QSqlQuery query;
    if(!query.exec(sql))
    {
      QMessageBox::about(this,"提示","添加管理员错误");
    }
    else
    {
        QMessageBox::about(this,"提示","恭喜,又增添一名管理员!");
    }
    model->select();//查询所有的数据
}

5、当日考勤统计

该模块功能是显示当天已打卡和未打卡的用户,如果未打卡会用红色标记,并统计打卡和未打卡人数,并通过这两个数据绘制饼状图。今天已打卡的用户,只需要在考勤打卡表中查找,今天未打卡的用户需要通过用户表以及考勤打卡表进行查找。

已打卡用户的查找

 QString sql = QString("select Stu.Sno,Stu.name,Attend.Time from Stu,Attend where Stu.Sno= Attend.Sno and Attend.Time like '%1'")
            .arg(Time);
    QSqlQuery query(sql);
    while(query.next())
    {
        QListWidgetItem *item = new QListWidgetItem();
        //创建显示对象
        Showrecordform *w = new Showrecordform();
        w->setattend(0,query.value(0).toString(),query.value(1).toString(),query.value(2).toString());
        //设置每一行item尺寸
        item->setSizeHint(w->sizeHint());
        ui->listWidget->addItem(item);
        //把item的窗口设置为w
        ui->listWidget->setItemWidget(item, w);
         qDebug()<<query.value(0).toInt()<<query.value(1).toString()<<query.value(2).toString();
        num++;

    }

未打卡用户是通过用户表中的总用户 减去 今天已打卡的用户,sql语句如下

 //未打卡学生
    //select Stu.Sno, Stu.name from Stu where Stu.Sno not in (select Sno from Attend where Time like '08-15%')
    QString sql_1 =QString("select Stu.Sno,Stu.name from Stu where Stu.Sno not in (select Sno from Attend where Time like '%1')").arg(Time);

    QSqlQuery query_1(sql_1);
    while(query_1.next())
    {
        QListWidgetItem *item = new QListWidgetItem();
        //创建显示对象
        Showrecordform *w = new Showrecordform();
        w->setattend(1,query_1.value(0).toString(),query_1.value(1).toString(),"未打卡");
        //设置每一行item尺寸
        item->setSizeHint(w->sizeHint());
        ui->listWidget->addItem(item);
        //把item的窗口设置为w
        ui->listWidget->setItemWidget(item, w);
        num1++;
        qDebug()<<query_1.value(0).toInt()<<query_1.value(1).toString();
        //ui->textBrowser->append(str);

    }

通过QChart模块进行数据可视化,使用这一模块需要在安装QT Create 时选择上QT Chart,并在配置文件添加QT += charts,在界面设计时,使用Graphics View这个组件显示饼状图,提前需要将这个组件提升为QtChart::QChartView。

 int a = (num*1.0/(num+num1))*100;
 int b = (num1*1.0/(num+num1))*100;
 QString str = QString("已打卡:%1%").arg(a);
 QString str1 = QString("未打卡:%1%").arg(b);
 QPieSeries * series = new QPieSeries();
 // 构造两个饼状分区
 QPieSlice *slice_1 = new QPieSlice(str, a, this);
 slice_1->setLabelVisible(true); // 显示饼状区对应的数据label
 slice_1->setBrush(Qt::green);

 QPieSlice *slice_2 = new QPieSlice(str1, b, this);
 slice_2->setLabelVisible(true);
 slice_2->setBrush(Qt::blue);

 series->append(slice_1);
 series->append(slice_2);
 series->slices().at(0)->setColor(QColor(13,217,152));   //设置颜色
 series->slices().at(0)->setLabelColor(QColor(13,217,152));

 series->slices().at(1)->setColor(QColor(255,0,0));
 series->slices().at(1)->setLabelColor(QColor(255,0,0));

 QChart *chart = new QChart();
 chart->setTheme(QChart::ChartThemeLight);
 chart->legend()->hide();
 chart->addSeries(series);
 chart->createDefaultAxes();
 chart->setTitleBrush(QBrush(QColor(47, 155, 164)));//设置标题Brush
 chart->setTitleFont(QFont("微软雅黑"));//设置标题字体
 chart->setTitle("当日考勤");
 chart->setAnimationOptions(QChart::AllAnimations); // 设置显示时的动画效果
 ui->widget->setChart(chart);    //将图显示在Graphics View上

该模块的效果图

6、通知栏信息的管理发布

这个模块相对于很简单,只用对通知信息表(Release)进行操作即可,实现对通知的发布、显示以及删除。显示通知的操作也是用数据模型QSqlTableModel来进行表和tableview进行绑定,从而达到显示的效果。

发布消息,即为将输入的标题、内容和日期存入通知信息表。代码如下

//将通知消息保存到数据库
void Notice::on_sendbt_clicked()
{
    if(ui->titleedit->text() == nullptr || ui->messageedit->toPlainText() == nullptr)
    {
        QMessageBox::about(this,"提示","请编辑完整再发送");
        return ;
    }
    //判断信息是否存在,存在则不创建
   QString sql_select = QString("select * from Release where title='%1'").arg(ui->titleedit->text());
   QSqlQuery query_select(sql_select);
   while(query_select.next())
   {
       QMessageBox::about(this,"提示","通知已存在!请重新编辑");
       //qDebug()<<"通知已存在!请重新编辑";
       return ;
   }
   //获取当前时间
   QString time = QDateTime::currentDateTime().toString("MM-dd");
   //将用户名和密码添加到user表中
   QString sql = QString("insert into Release(title,content,Time) values('%1','%2','%3')")
          .arg(ui->titleedit->text())
          .arg(ui->messageedit->toPlainText())
          .arg(time);
    QSqlQuery query;
    if(!query.exec(sql))
    {
      QMessageBox::about(this,"提示","通知发布出现错误");
    }
    else
    {
        QMessageBox::about(this,"提示","恭喜,通知成功发布!");
        model->select();//查询所有的数据
    }
}

删除通知,通过标题进行删除

//通过标题对消息进行删除
void Notice::on_deletebt_clicked()
{
    if(ui->titleclearedit ->text() == nullptr)
    {
        QMessageBox::about(this,"提示","请输入标题再删除!");
        return ;
    }
   //判断信息是否存在,不存在则不进行删除
   QString sql_select = QString("select * from Release where title='%1'").arg(ui->titleclearedit->text());
   QSqlQuery query_select(sql_select);
   while(query_select.next())
   {
       QString sql = QString("delete from Release where title='%1'").arg(ui->titleclearedit->text());
       QSqlQuery query_select(sql);

       QMessageBox::about(this,"提示","成功删除该类通知");

       model->select();//查询所有的数据
       return ;
   }
   QMessageBox::about(this,"提示","该类通知未查到,删除失败");
}

至此我们的基于QT的人脸识别考勤管理系统正式完成了,在这个项目中用到的技术只有QT,opencv、sqlite3。相信你们同样也能做出来,欢迎大家评论留言。

问题咨询及项目源码请加群:

QQ群

名称:IT项目交流群

群号:245022761

  • 18
    点赞
  • 186
    收藏
    觉得还不错? 一键收藏
  • 27
    评论
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值