服务端
.pro
QT += core gui network sql
#window平台opencv,seetaface环境
win32{
LIBS +=C:\opencv452\x64\mingw\lib\libopencv*
LIBS +=C:\SeetaFace\lib\libSeeta*
INCLUDEPATH +=C:\opencv452\include
INCLUDEPATH += C:\opencv452\include\opencv2
INCLUDEPATH += C:\SeetaFace\include
INCLUDEPATH += C:\SeetaFace\include\seeta
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <qfaceobject.h>
#include <QDebug>
#include <QSqlTableModel>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QSqlError>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void query(cv::Mat& image);
protected slots:
void accept_client();
void read_data();
void recv_faceid(int64_t faceid);
private:
Ui::MainWindow *ui;
QTcpServer mserver;
QTcpSocket *msocket;
quint64 bsize;
qFaceObject fobj;
QSqlTableModel model;
};
#endif // MAINWINDOW_H
qfaceobject.h
#ifndef QFACEOBJECT_H
#define QFACEOBJECT_H
#include <QObject>
#include <seeta/FaceEngine.h>
#include <opencv.hpp>
#include <QDebug>
// 人脸数据存储,人脸检测,人脸识别
class qFaceObject : public QObject
{
Q_OBJECT
public:
explicit qFaceObject(QObject *parent = nullptr);
~qFaceObject();
public slots:
int64_t face_register(cv::Mat& faceImage); // 人脸注册
int face_query(cv::Mat& faceImage); // 人脸查询
signals:
void send_faceid(int64_t faceid);
private:
seeta::FaceEngine *fengineptr;
};
#endif // QFACEOBJECT_H
registerwin.h
#ifndef REGISTERWIN_H
#define REGISTERWIN_H
#include <QWidget>
#include <opencv.hpp>
namespace Ui {
class RegisterWin;
}
class RegisterWin : public QWidget
{
Q_OBJECT
public:
explicit RegisterWin(QWidget *parent = nullptr);
~RegisterWin();
void timerEvent(QTimerEvent *e);
private slots:
void on_resetButton_clicked();
void on_addpicButton_clicked();
void on_registerButton_clicked();
void on_videoswitchButton_clicked();
void on_cameraButton_clicked();
private:
Ui::RegisterWin *ui;
int timerid;
cv::VideoCapture cap;
cv::Mat image;
};
#endif // REGISTERWIN_H
selectwin.h
#ifndef SELECTWIN_H
#define SELECTWIN_H
#include <QWidget>
#include <QSqlTableModel>
//#include <QSqlQueryModel>
//#include <QSortFilterProxyModel>
#include <QDebug>
namespace Ui {
class SelectWin;
}
class SelectWin : public QWidget
{
Q_OBJECT
public:
explicit SelectWin(QWidget *parent = nullptr);
~SelectWin();
private slots:
void on_pushButton_clicked();
private:
Ui::SelectWin *ui;
QSqlTableModel *model;
// QSqlQueryModel *model;
// QSortFilterProxyModel *proxyModel;
};
#endif // SELECTWIN_H
sqlite数据库存储数据
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <registerwin.h>
#include <selectwin.h>
#include <opencv.hpp>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qRegisterMetaType<cv::Mat>("cv::Mat&");
qRegisterMetaType<cv::Mat>("cv::Mat");
qRegisterMetaType<int64_t>("int64_t");
// RegisterWin ww;
// ww.show();
// SelectWin sw;
// sw.show();
// 连接数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
// 设置数据库名称
db.setDatabaseName("server.db");
// 打开数据库
if(!db.open())
{
qDebug() <<db.lastError().text();
return -1;
}
// 创建员工信息表格
QString createsql = "create table if not exists employee(employeeID integer primary key autoincrement, name varchar(256), sex varchar(32),"
"birthday text, address text, phone text, faceID integer unique, headfile text)";
QSqlQuery query;
if(!query.exec(createsql))
{
qDebug() << query.lastError().text();
return -1;
}
// 考勤表格
createsql = "create table if not exists attendance(attendanceID integer primary key autoincrement, employeeID integer,"
"attendanceTime TimeStamp NOT NULL DEFAULT(datetime('now','localtime')))";
if(!query.exec(createsql))
{
qDebug() << query.lastError().text();
return -1;
}
MainWindow w;
w.show();
return a.exec();
}
线程:处理识别face任务
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDateTime>
#include <opencv.hpp>
#include <QThread>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowIcon(QIcon(":/z2.jpg"));
//qtcpServer 当有客户端连会发送newconnection信号
connect(&mserver, &QTcpServer::newConnection, this, &MainWindow::accept_client);
mserver.listen(QHostAddress::Any, 9898); // 监听 启动服务器
bsize = 0;
// 给sql模型绑定表格
model.setTable("employee");
// 创建一个线程
QThread *thread = new QThread();
// 把qFaceObject fobj对象移动到thread线程中执行
fobj.moveToThread(thread);
thread->start(); // 启动线程
connect(this, &MainWindow::query, &fobj, &qFaceObject::face_query);
// 关联QFaceObject对象里面的send_faceid信号
connect(&fobj, &qFaceObject::send_faceid, this, &MainWindow::recv_faceid);
}
MainWindow::~MainWindow()
{
delete ui;
}
// 接受客户端连接
void MainWindow::accept_client()
{
qDebug() << "新客户端连上了";
// 获取与客户端通信的套接字
msocket = mserver.nextPendingConnection();
// 当客户端有数据到达会发送readyRead信号
connect(msocket, &QTcpSocket::readyRead, this, &MainWindow::read_data);
}
// 读取客户端发送的数据
void MainWindow::read_data()
{
//读取所有的数据
// QString msg = msocket->readAll();
// qDebug() <<msg;
QDataStream stream(msocket); // 套接字绑定到数据流
stream.setVersion(QDataStream::Qt_5_14);
if(bsize == 0){
if(msocket->bytesAvailable() < (qint64)sizeof(bsize)) return;
// 采集数据的长度
stream>>bsize;
}
if(msocket->bytesAvailable() <(qint64)bsize) // 说明数据还没有发送完成,返回继续等待
{
return;
}
QByteArray data;
stream>>data;
bsize = 0;
if(data.size() == 0) // 没有读到数据
{
return;
}
// 显示图片
QPixmap mmp;
mmp.loadFromData(data,"jpg");
mmp = mmp.scaled(ui->piclb->size());
ui->piclb->setPixmap(mmp);
// 识别人脸 QByteArray转Mat格式
cv::Mat faceImage;
std::vector<uchar> decode;
decode.resize(data.size());
memcpy(decode.data(), data.data(), data.size());
faceImage = cv::imdecode(decode, cv::IMREAD_COLOR);
// int faceid = fobj.face_query(faceImage); // 消化资源较多
emit query(faceImage);
}
void MainWindow::recv_faceid(int64_t faceid)
{
// qDebug() << "人脸id是:"<<faceid;
// 从数据库中查询faceid对应的个人信息
qDebug() <<"识别到的人脸-> id:" <<faceid;
if(faceid < 0)
{
QString sdmsg = QString("{\"employeeID\": \"\" , \"name\": \"\", \"department\": \"\", \"time\": \"\"}");
msocket->write(sdmsg.toUtf8()); // 把打包好的 空 数据发送给客户端
return ;
}
// 给模型设置过滤器
model.setFilter(QString("faceID=%1").arg(faceid));
// 查询
model.select();
// 判断是否查询到数据
if(model.rowCount() == 1)
{
// 工号,姓名,部门,时间
// {employeeID:%1, name:%2, department:软件, time:%3}
QSqlRecord record = model.record(0);
QString sdmsg = QString("{\"employeeID\":\"%1\", \"name\":\"%2\", \"department\":\"软件\", \"time\":\"%3\"}")
.arg(record.value("employeeID").toString()).arg(record.value("name").toString())
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
// msocket->write(sdmsg.toUtf8()); // 把打包好的数据发送给客户端
// 把数据写入数据库--考勤表 attendance
QString insertSql = QString("insert into attendance(employeeID) values('%1')").arg(record.value("employeeID").toString());
QSqlQuery query;
if(!query.exec(insertSql))
{
QString sdmsg = QString("{\"employeeID\": \"\" , \"name\": \"\", \"department\": \"\", \"time\": \"\"}");
msocket->write(sdmsg.toUtf8()); // 把打包好的 空 数据发送给客户端
qDebug() <<query.lastError().text();
return ;
}else
{
msocket->write(sdmsg.toUtf8()); // 把打包好的数据发送给客户端
}
}
}
封装识别、注册功能
qfaceobject.cpp
#include "qfaceobject.h"
qFaceObject::qFaceObject(QObject *parent) : QObject(parent)
{
// 初始化
seeta::ModelSetting FDmode("C:/SeetaFace/bin/model/fd_2_00.dat",seeta::ModelSetting::CPU,0);
seeta::ModelSetting PDmode("C:/SeetaFace/bin/model/pd_2_00_pts5.dat",seeta::ModelSetting::CPU,0);
seeta::ModelSetting FRmode("C:/SeetaFace/bin/model/fr_2_10.dat",seeta::ModelSetting::CPU,0);
this->fengineptr = new seeta::FaceEngine(FDmode, PDmode, FRmode);
// 导入已有的人脸数据库
this->fengineptr->Load("./face.db");
}
// 析构函数
qFaceObject::~qFaceObject()
{
delete fengineptr;
}
int64_t qFaceObject::face_register(cv::Mat &faceImage)
{
// 把OpenCV的Mat数据转为seetaface的数据
SeetaImageData simage;
simage.data = faceImage.data;
simage.width = faceImage.cols;
simage.height = faceImage.rows;
simage.channels = faceImage.channels();
int64_t faceid = this->fengineptr->Register(simage); // 注册返回一个人脸id
qDebug() << "faceid是:" <<faceid;
if(faceid >= 0){
fengineptr->Save("./face.db");
}
return faceid;
}
int qFaceObject::face_query(cv::Mat &faceImage)
{
// 把OpenCV的Mat数据转为seetaface的数据
SeetaImageData simage;
simage.data = faceImage.data;
simage.width = faceImage.cols;
simage.height = faceImage.rows;
simage.channels = faceImage.channels();
float similarity = 0;
int64_t faceid = fengineptr->Query(simage,&similarity); // 运行时间比较长
qDebug() <<"查询:"<<faceid <<similarity;
// if(similarity > 0.85) // 相似度
if(similarity > 0.7)
{
emit send_faceid(faceid);
}else {
emit send_faceid(-1);
}
return faceid;
}
registerwin.cpp
#include "registerwin.h"
#include "ui_registerwin.h"
#include <QFileDialog>
#include <qfaceobject.h>
#include <QSqlTableModel>
#include <QSqlRecord>
#include <QMessageBox>
RegisterWin::RegisterWin(QWidget *parent) :
QWidget(parent),
ui(new Ui::RegisterWin)
{
ui->setupUi(this);
}
RegisterWin::~RegisterWin()
{
delete ui;
}
void RegisterWin::timerEvent(QTimerEvent *e)
{
Q_UNUSED(e);
// 获取摄像头数据,并且显示在界面上
if(cap.isOpened())
{
cap>>image;
if(image.data == nullptr) return;
}
// Mat --> QImage
cv::Mat rgbImage;
cv::cvtColor(image, rgbImage, cv::COLOR_BGR2RGB);
QImage qImg(rgbImage.data, rgbImage.cols, rgbImage.rows, rgbImage.step1(), QImage::Format_RGB888);
// 在qt界面上显示
QPixmap mmp = QPixmap::fromImage(qImg);
mmp = mmp.scaledToWidth(ui->pichead->width());
ui->pichead->setPixmap(mmp);
}
void RegisterWin::on_resetButton_clicked()
{
// 清空数据
ui->nameEdit->clear();
// ui->birthdaydateEdit->clear();
ui->birthdaydateEdit->setDate(QDate::currentDate());
ui->addressEdit->clear();
ui->phoneEdit->clear();
ui->picfileEdit->clear();
}
void RegisterWin::on_addpicButton_clicked()
{
// 通过文件对话框,选择文件路径
QString filepath = QFileDialog::getOpenFileName(this);
ui->picfileEdit->setText(filepath);
// 显示图片
QPixmap mmp(filepath);
mmp = mmp.scaledToWidth(ui->pichead->width()); // 根据框的宽度,设置图片的宽度,图片高度自适应
ui->pichead->setPixmap(mmp);
}
void RegisterWin::on_registerButton_clicked()
{
// 1、通过照片,结合faceObject模块,得到faceID
qFaceObject faceobj;
cv::Mat image = cv::imread(ui->picfileEdit->text().toUtf8().data());
int faceID = faceobj.face_register(image);
// 把头像保存到一个固定路径下
QString headfile = QString("./data/%1.jpg").arg(QString(ui->nameEdit->text().toUtf8().toBase64()));
cv::imwrite(headfile.toUtf8().data(), image);
// 2、把个人信息存储到数据库表employee
QSqlTableModel model;
model.setTable("employee"); // 设置表名
QSqlRecord record = model.record();
// 设置数据
record.setValue("name", ui->nameEdit->text());
record.setValue("sex", ui->manradioButton->isChecked()?"男":"女");
record.setValue("birthday",ui->birthdaydateEdit->text());
record.setValue("address",ui->addressEdit->text());
record.setValue("phone",ui->phoneEdit->text());
record.setValue("faceID",faceID);
// 头像路径
record.setValue("headfile",headfile);
// 把记录插入到数据库表格中
bool ret = model.insertRecord(0,record);
// 3、提示注册成功
if(ret)
{
QMessageBox::information(this,"注册提示","注册成功");
// 提交
model.submitAll();
}else {
QMessageBox::information(this,"注册提示","注册失败");
}
}
void RegisterWin::on_videoswitchButton_clicked()
{
if(ui->videoswitchButton->text() == "打开摄像头")
{
// 打开摄像头
if(cap.open(0))
{
ui->videoswitchButton->setText("关闭摄像头");
timerid = startTimer(100); // 启动定时器事件
}
}else
{
killTimer(timerid); // 关闭定时器事件
ui->videoswitchButton->setText("打开摄像头");
// 关闭摄像头
cap.release();
}
}
void RegisterWin::on_cameraButton_clicked()
{
// 保存数据
// 把头像保存到一个固定路径下
QString headfile = QString("./data/%1.jpg").arg(QString(ui->nameEdit->text().toUtf8().toBase64()));
ui->picfileEdit->setText(headfile);
cv::imwrite(headfile.toUtf8().data(), image);
killTimer(timerid); // 关闭定时器事件
ui->videoswitchButton->setText("打开摄像头");
// 关闭摄像头
cap.release();
}
selectwin.cpp
#include "selectwin.h"
#include "ui_selectwin.h"
SelectWin::SelectWin(QWidget *parent) :
QWidget(parent),
ui(new Ui::SelectWin)
{
ui->setupUi(this);
//model = new QSqlTableModel();
}
SelectWin::~SelectWin()
{
delete ui;
}
void SelectWin::on_pushButton_clicked()
{
model = new QSqlTableModel();
if(ui->radioButton->isChecked())
{
model->setTable("employee");
}
if(ui->radioButton_2->isChecked())
{
model->setTable("attendance");
}
// 设置过滤器
// model->setFilter("name='张三'");
//查询
model->select();
ui->tableView->setModel(model);
// qDebug()<< model->rowCount();
// qDebug() << "查询";
/*
QString cmd;
model = new QSqlQueryModel(this);
cmd += "select * from employee ";
model->setQuery(cmd);
proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(model);
ui->tableView->setModel(proxyModel);
*/
}
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>664</width>
<height>420</height>
</rect>
</property>
<property name="windowTitle">
<string>服务端</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>考勤</string>
</attribute>
<widget class="QLabel" name="piclb">
<property name="geometry">
<rect>
<x>20</x>
<y>40</y>
<width>321</width>
<height>271</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 170, 127);</string>
</property>
<property name="text">
<string>考勤图像</string>
</property>
</widget>
</widget>
<widget class="RegisterWin" name="tab_2">
<attribute name="title">
<string>注册</string>
</attribute>
</widget>
<widget class="SelectWin" name="tab_3">
<attribute name="title">
<string>查询</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>RegisterWin</class>
<extends>QWidget</extends>
<header location="global">registerwin.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>SelectWin</class>
<extends>QWidget</extends>
<header location="global">selectwin.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
registerwin.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RegisterWin</class>
<widget class="QWidget" name="RegisterWin">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>525</width>
<height>344</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>姓名:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="nameEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>性别:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="manradioButton">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>男</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="womanradioButton_2">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>女</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>生日:</string>
</property>
</widget>
</item>
<item>
<widget class="QDateEdit" name="birthdaydateEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>地址:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addressEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>电话:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="phoneEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QPushButton" name="registerButton">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>注册</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="resetButton">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>重置</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="4,1,0">
<item>
<widget class="QLabel" name="pichead">
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 170, 255);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="picfileEdit"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QPushButton" name="addpicButton">
<property name="font">
<font>
<pointsize>7</pointsize>
</font>
</property>
<property name="text">
<string>添加头像</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="videoswitchButton">
<property name="font">
<font>
<pointsize>7</pointsize>
</font>
</property>
<property name="text">
<string>打开摄像头</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cameraButton">
<property name="font">
<font>
<pointsize>7</pointsize>
</font>
</property>
<property name="text">
<string>拍照</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
selectwin.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SelectWin</class>
<widget class="QWidget" name="SelectWin">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>569</width>
<height>408</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QTableView" name="tableView">
<property name="geometry">
<rect>
<x>10</x>
<y>60</y>
<width>511</width>
<height>281</height>
</rect>
</property>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>341</width>
<height>30</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="2,2,0">
<item>
<widget class="QRadioButton" name="radioButton">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>员工</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_2">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>考勤</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>查询</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
客户端
facetest.pro
QT += core gui network
#window平台opencv,seetaface环境
win32{
LIBS +=C:\opencv452\x64\mingw\lib\libopencv*
LIBS +=C:\SeetaFace\lib\libSeeta*
INCLUDEPATH +=C:\opencv452\include
INCLUDEPATH += C:\opencv452\include\opencv2
INCLUDEPATH += C:\SeetaFace\include
INCLUDEPATH += C:\SeetaFace\include\seeta
}
#linux平台opencv seetaface环境
unix{
LIBS += -L/opt/opencv4-pc/lib -lopencv_world \
-lSeetaFaceDetector \
-lSeetaFaceLandmarker \
-lSeetaFaceRecognizer \
-lSeetaFaceTracker \
-lSeetaNet \
-lSeetaQualityAssessor \
INCLUDEPATH += /opt/opencv4-pc/include/opencv4
INCLUDEPATH += /opt/opencv4-pc/include/opencv4/opencv2
INCLUDEPATH += /opt/opencv4-pc/include
INCLUDEPATH += /opt/opencv4-pc/include/seeta
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <opencv.hpp>
#include <QTcpSocket>
#include <QTimer>
using namespace cv;
using namespace std;
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
// 定时器 事件
void timerEvent(QTimerEvent *e);
protected slots:
void recv_data();
private slots:
void timer_connect(); // 用定时器定时的去连接服务器
void stop_connect();
void start_connect();
private:
Ui::MainWindow *ui;
// 摄像头
VideoCapture cap;
// haar--级联分类器
cv::CascadeClassifier cascade;
// 创建网络套接字,定时器
QTcpSocket msocket;
QTimer mtimer;
// 标志是否是同一个人脸进入到识别区域
int flag;
// 保存人脸的数据
cv::Mat faceMat;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QImage>
#include <QPainter>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QJsonObject>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowIcon(QIcon(":/z1.jpg"));
// 打开摄像头
cap.open(0); // Linux下是/dev/video0-7
// 启动定时器 事件
startTimer(100);
// 导入级联分类器文件 人脸检测
cascade.load("C:/opencv452/etc/haarcascades/haarcascade_frontalface_alt2.xml");
// QTcpSocket 当断开连接的时候disconnected信号,连接成功会发送connected信号
connect(&msocket, &QTcpSocket::disconnected, this, &MainWindow::start_connect);
connect(&msocket, &QTcpSocket::connected, this, &MainWindow::stop_connect);
// 关联接收数据的槽函数
connect(&msocket, &QTcpSocket::readyRead, this, &MainWindow::recv_data);
// 定时器连接服务器
connect(&mtimer, &QTimer::timeout, this, &MainWindow::timer_connect);
// 启动定时器
mtimer.start(5000); // 每5s连接一次,直到连接成功就不再连接
flag = 0;
ui->widget_2->hide();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::recv_data()
{
// {employeeID:%1, name:%2, department:软件, time:%3}
// QString msg = msocket.readAll();
// qDebug() << msg;
// Json解析
QByteArray array = msocket.readAll();
qDebug() << array;
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(array, &err);
if(err.error != QJsonParseError::NoError)
{
qDebug() <<"json数据错误";
return ;
}
QJsonObject obj = doc.object();
QString employeeID = obj.value("employeeID").toString();
QString name = obj.value("name").toString();
QString department = obj.value("department").toString();
QString timestr = obj.value("time").toString();
ui->label_7->setText(employeeID);
ui->label_9->setText(name);
ui->label_11->setText(department);
ui->label_13->setText(timestr);
// 把 OpenCV里面的Mat数据格式(BGR),转qt里面的QImage(RGB)
// cvtColor(faceMat,faceMat, COLOR_BGR2RGB); // 将一种颜色空间的图像转换为另一种颜色空间的图像
// QImage image(faceMat.data, faceMat.cols, faceMat.rows, faceMat.step1(), QImage::Format_RGB888);
// QPixmap mmp = QPixmap::fromImage(image);
// ui->label_5->setPixmap(mmp);
// 通过样式显示图片
ui->label_5->setStyleSheet("border-radius: 50%; border-image: url(./face.jpg);");
ui->widget_2->show();
}
void MainWindow::timerEvent(QTimerEvent *e)
{
Q_UNUSED(e);
// 采集数据
Mat srcImage;
if(cap.grab())
{
cap.read(srcImage); // 读取一帧数据
}
// 把图片大小设置与显示窗口一样大
cv::resize(srcImage, srcImage, Size(401,371));
Mat grayImage; // 定义灰度图像
cvtColor(srcImage,grayImage,COLOR_BGRA2GRAY ); // 将一种颜色空间的图像转换为另一种颜色空间的图像
// 检测人脸数据
std::vector<Rect> faceRects;
// cascade.detectMultiScale( srcImage, faceRects, 1.1, 3, 0, cv::Size(200,200));
// cascade.detectMultiScale( srcImage, faceRects);
cascade.detectMultiScale( grayImage, faceRects); // 检测人脸
if(faceRects.size() > 0 && flag >=0) // 检测到人脸
{
Rect rect = faceRects.at(0); // 第一个人脸的矩形框
rectangle(srcImage, rect, Scalar(0,0,255)); // 绘制
// 移动人脸框
ui->headpiclb->move(rect.x, rect.y/2);
if (flag > 2){
// 把Mat数据转化为QbyteArray 先编码成jpg格式
std::vector<uchar> buf;
cv::imencode(".jpg", srcImage, buf);
QByteArray byte((const char*)buf.data(), buf.size()); // 强转为字节数组
// 准备发送: 发文件的大小,发文件
quint64 backsize = byte.size();
QByteArray sendData;
QDataStream stream(&sendData, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_5_14);
stream<<backsize<<byte;
// 发送
msocket.write(sendData);
flag = -2;
faceMat = srcImage(rect);
// 保存
imwrite("./face.jpg",faceMat);
}
flag++;
}
if(faceRects.size() == 0)
{
// 把人脸框移动到中心位置
ui->headpiclb->move(100,80);
ui->widget_2->hide();
flag = 0;
}
/*
参数1:image--待检测图片,一般为灰度图像加快检测速度;
参数2:objects--被检测物体的矩形框向量组;
参数3:scaleFactor--表示在前后两次相继的扫描中,搜索窗口的比例系数。默认为1.1即每次搜索窗口依次扩大10%;
参数4:minNeighbors--表示构成检测目标的相邻矩形的最小个数(默认为3个)。
如果组成检测目标的小矩形的个数和小于 min_neighbors - 1 都会被排除。
如果min_neighbors 为 0, 则函数不做任何操作就返回所有的被检候选矩形框,
这种设定值一般用在用户自定义对检测结果的组合程序上;
参数5:flags--要么使用默认值,要么使用CV_HAAR_DO_CANNY_PRUNING,如果设置为
CV_HAAR_DO_CANNY_PRUNING,那么函数将会使用Canny边缘检测来排除边缘过多或过少的区域,因此这些区域通常不会是人脸所在区域;
参数6、7:minSize和maxSize用来限制得到的目标区域的范围。
*/
if(srcImage.data == nullptr) return;
// 把 OpenCV里面的Mat数据格式(BGR),转qt里面的QImage(RGB)
cvtColor(srcImage,srcImage, COLOR_BGR2RGB); // 将一种颜色空间的图像转换为另一种颜色空间的图像
QImage image(srcImage.data, srcImage.cols, srcImage.rows, srcImage.step1(), QImage::Format_RGB888);
QPixmap mmp = QPixmap::fromImage(image);
ui->videolb->setPixmap(mmp);
}
void MainWindow::timer_connect()
{
msocket.connectToHost("127.0.0.1",9898);
qDebug()<<"正在连接服务器";
}
void MainWindow::stop_connect()
{
mtimer.stop(); // 停止定时器
qDebug()<<"成功连接服务器";
}
void MainWindow::start_connect()
{
mtimer.start(5000); // 启动定时器
qDebug()<<"断开连接";
}
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>674</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>客户端</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>401</width>
<height>371</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<widget class="QLabel" name="headpiclb">
<property name="geometry">
<rect>
<x>100</x>
<y>80</y>
<width>200</width>
<height>200</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/crc2.png);
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="videolb">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>401</width>
<height>371</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<zorder>videolb</zorder>
<zorder>headpiclb</zorder>
</widget>
<widget class="QWidget" name="widget_2" native="true">
<property name="geometry">
<rect>
<x>30</x>
<y>390</y>
<width>300</width>
<height>40</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(207, 185, 255);
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(9, 41, 4, 255), stop:0.085 rgba(2, 79, 0, 255), stop:0.19 rgba(50, 147, 22, 255), stop:0.275 rgba(236, 191, 49, 255), stop:0.39 rgba(243, 61, 34, 255), stop:0.555 rgba(135, 81, 60, 255), stop:0.667 rgba(121, 75, 255, 255), stop:0.825 rgba(164, 255, 244, 255), stop:0.885 rgba(104, 222, 71, 255), stop:1 rgba(93, 128, 0, 255));</string>
</property>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>60</x>
<y>5</y>
<width>30</width>
<height>30</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/yes.jpg);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>110</x>
<y>5</y>
<width>80</width>
<height>30</height>
</rect>
</property>
<property name="font">
<font>
<family>Arial</family>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>认证成功</string>
</property>
</widget>
</widget>
<widget class="QWidget" name="widget_3" native="true">
<property name="geometry">
<rect>
<x>420</x>
<y>70</y>
<width>250</width>
<height>360</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">QWidget{
border:1px solid #313238;
background-color: rgb(30, 47, 49);
}
QWidget#label_5 {
border-radius: 50%;
}</string>
</property>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>50</x>
<y>10</y>
<width>161</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<family>Arial</family>
<pointsize>16</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>ArrowCursor</cursorShape>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(255, 255, 255);</string>
</property>
<property name="text">
<string>识别测试系统</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>60</x>
<y>40</y>
<width>145</width>
<height>145</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(23, 37, 39);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>190</y>
<width>201</width>
<height>151</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="widget_4" native="true">
<property name="styleSheet">
<string notr="true">color: rgb(255, 255, 255);</string>
</property>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>181</width>
<height>22</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,3">
<item>
<widget class="QLabel" name="label_6">
<property name="enabled">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(82, 25, 255);</string>
</property>
<property name="text">
<string>编号</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="enabled">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">font: 12pt "Arial";</string>
</property>
<property name="text">
<string>010001</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_5" native="true">
<property name="styleSheet">
<string notr="true">color: rgb(255, 255, 255);</string>
</property>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>181</width>
<height>22</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,3">
<item>
<widget class="QLabel" name="label_8">
<property name="enabled">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(82, 25, 255);</string>
</property>
<property name="text">
<string>姓名</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_9">
<property name="enabled">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">font: 12pt "Arial";</string>
</property>
<property name="text">
<string>010001</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_6" native="true">
<property name="styleSheet">
<string notr="true">color: rgb(255, 255, 255);</string>
</property>
<widget class="QWidget" name="layoutWidget_2">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>181</width>
<height>22</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="1,3">
<item>
<widget class="QLabel" name="label_10">
<property name="enabled">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(82, 25, 255);</string>
</property>
<property name="text">
<string>部门</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="enabled">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">font: 12pt "Arial";</string>
</property>
<property name="text">
<string>010001</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_7" native="true">
<property name="styleSheet">
<string notr="true">color: rgb(255, 255, 255);</string>
</property>
<widget class="QWidget" name="layoutWidget_3">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>181</width>
<height>22</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="1,3">
<item>
<widget class="QLabel" name="label_12">
<property name="enabled">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(82, 25, 255);</string>
</property>
<property name="text">
<string>时间</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="enabled">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">font: 12pt "Arial";</string>
</property>
<property name="text">
<string>010001</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>