1.QT完成人脸识别
pro文件:
头文件:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
#include<opencv2/face.hpp>
#include <vector>
#include <map>
#include <QMessageBox>
#include <QDebug>
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QTimerEvent>
#include<QtSerialPort/QtSerialPort>
#include<QtSerialPort/QSerialPortInfo>
using namespace cv;
using namespace cv::face;
using namespace std;
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_openCamerBtn_clicked();
void on_closeCamerBtn_clicked();
void on_inputFaceBtn_clicked();
private:
Ui::Widget *ui;
/***************第一模块:关于摄像头的相关组件*******************/
VideoCapture v; //视频流对象
Mat src; //原图像
Mat rgb; //存放rgb图像,因为QT能识别的图像色彩空间位rgb
Mat gray; //灰度图
Mat dst; //均衡化图像
CascadeClassifier c;//级联分类器
vector<Rect>faces; //存储人脸矩形区域的容器
int cameraId; //摄像头的定时器
void timerEvent(QTimerEvent *event); //定时器事件处理函数
/**************第二模块:录入人脸的先关组件*********************/
Ptr<FaceRecognizer> recognizer; //人脸识别器
vector<Mat> study_face; //要录入的人脸容器
vector<int> study_lab; //要录入的人脸的标签
int studyId; //人脸录入的定时器
int flag; //标识是否正在录入人脸
int count; //记录学习的次数
/*************第三模块:人脸检测相关组件*********************/
int checkId; //人脸检测定时器
};
#endif // WIDGET_H
自定义函数:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//将登录按钮设为不可用
ui->loginBtn->setEnabled(false);
//启动摄像头
if(!v.open(0))
{
QMessageBox::information(this,"错误","打开摄像头失败");
return;
}
//将级联分类器加载进来
if(!c.load("D:/opencv/res/haarcascade_frontalface_alt2.xml"))
{
QMessageBox::information(this,"失败","人脸识别模型装载失败");
return ;
}
//配置人脸识别器
QFile file("D:/opencv/res/myFace.xml");
//判断文件是否存在,如果存在则直接下载,如果文件不存在,则创建一个人脸识别器
if(file.exists())
{
//人脸模型存在,直接下载
recognizer = FaceRecognizer::load<LBPHFaceRecognizer>("D:/opencv/res/myFace.xml");
}else
{
//人脸不存在,则需要创建
recognizer = LBPHFaceRecognizer::create();
}
//启动人脸检测的定时器
checkId = this->startTimer(3000);
//设置人脸识别的可信度
recognizer->setThreshold(100);
flag = 0; //开始时就处于检测
}
Widget::~Widget()
{
delete ui;
}
//打开摄像头对应的槽函数
void Widget::on_openCamerBtn_clicked()
{
//启动定时器
cameraId = this->startTimer(20);
ui->cameraLab->show();
}
//关闭摄像头
void Widget::on_closeCamerBtn_clicked()
{
//关闭定时器
this->killTimer(cameraId);
ui->cameraLab->hide();
}
//定时器事件处理函数
void Widget::timerEvent(QTimerEvent *event)
{
//判断是哪个定时器到位
if(event->timerId() == cameraId)
{
//1.从摄像头中读取一张图像
v.read(src);//得到一张原图
//2.将图像翻转
flip(src,src,1);//按照y轴翻转
//3.将src的bgr图像转换成rgb图像
cvtColor(src,rgb,CV_BGR2RGB);
//4.重新设置大小
cv::resize(rgb,rgb,Size(311,201));
//5.灰度处理
cvtColor(rgb,gray,CV_RGB2GRAY);
//6.均衡化处理
equalizeHist(gray,dst);
//7.使用级联分类器获取人脸矩形区域
c.detectMultiScale(dst,faces);
//8.将矩形框绘制到rgb图像上
for(int i=0;i<(int)faces.size();i++)
{
rectangle(rgb,faces[i],Scalar(255,0,0));
}
//9.使用rgb图像,将Mat图构造出一个QT能识别的图像
QImage img(rgb.data,rgb.cols,rgb.rows,rgb.cols*rgb.channels(),QImage::Format_RGB888);
//功能:通过其他图像构造一个QImage图像
//参数1:其他图像的数据
//参数2:图像的宽度
//参数3:图像的高度
//参数4:每一行的字节数
//参数5:图像格式,24位图,每一中颜色用8位表示
//10.将图像展示到lab中
ui->cameraLab->setPixmap(QPixmap::fromImage(img));
}
//判断是否是人脸录入定时器到位
if(event->timerId() == studyId)
{
//判断ui界面是否有矩形框
if(faces.empty()) return;
//判断脸识别器是否存在
if(recognizer.empty()) return;
//提示正在录入人脸
qDebug() << "正在录入人脸" ;
//获取ui界面中矩形框框起来的人脸区域
Mat face = src(faces[0]);
//将该图像进行重新设置大小
cv::resize(face,face,Size(100,100));
//灰度处理
cvtColor(face,face,CV_BGR2GRAY);
//均衡化处理
equalizeHist(face,face);
//将人脸放入学习容器中
study_face.push_back(face);
study_lab.push_back(1);
count++;//完成一次人脸的存放
if(count == 50) //已经收集50张人脸进行学习
{
count = 0;//以于下一次录入
//更新人脸模型
//参数1:要进行更新的人脸数组
//参数2:要更新的人脸标签数组
//返回值:无
recognizer->update(study_face,study_lab);
//将数据模型保存到本地磁盘中
recognizer->save("D:/opencv/res/myFace.xml");
//殿后工作
study_face.clear(); //清空人脸数组
study_lab.clear(); //清空标签数组
flag = 0; //表明录入以及结束,可以进行人脸检测
ui->inputFaceBtn->setEnabled(true); //按钮设置成可用状态
this->killTimer(studyId); //关闭人脸录入的定时器
QMessageBox::information(this,"成功","人脸录入成功");
}
}
//判断是否是人脸检测的定时器到位
if(event->timerId() == checkId)
{
qDebug() << "正在检测...";
//判断是否处于检测
if(flag == 0)
{
QFile file("D:/opencv/res/myFace.xml");
if(file.exists()) //表明人脸模型存在的基础上进行识别
{
if(faces.empty() || recognizer->empty()) return;//ui界面无矩形框或者没有人脸识别器
//到此表明可以进行检测
Mat face = src(faces[0]);
//重新设置大小,保持跟保存人脸时一致
cv::resize(face,face,Size(100,100));
//灰度处理
cvtColor(face,face,CV_BGR2GRAY);
//均衡化处理
equalizeHist(face,face);
//定义记录检测后返回结果的变量
int lab = -1; //返回图像的标签
double conf = 0.0; //返回图像的可信度
//将该人脸进行预测
recognizer->predict(face,lab,conf);
qDebug() << "lab=" << lab << "conf=" << conf;
//对人脸识别后的结果进行判断
if(lab!= -1)
{
ui->loginBtn->setEnabled("true");
QMessageBox::information(this,"成功","识别成功");
}
}
}
}
}
//录入人脸按钮对应的槽函数
void Widget::on_inputFaceBtn_clicked()
{
//启动人脸录入的定时器
qDebug() << "开始录入...";
studyId = this->startTimer(30);
//将按钮设置成不可用状态
ui->inputFaceBtn->setEnabled(false);
//将flag设置成1,表示正在录入人脸,不要进行人脸检测了
flag = 1;
count = 0;//清空计数器
}
主函数:
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
ui界面
运行结果:(可进行人脸录入,人脸识别)
面试题1:
c语言中的static和c++中的static的用法:
在c语言中:
1.static修饰的全局变量作用域限制在当前文件,无法被外部文件所引用。2.static修饰的局部变量延长生命周期,但不改变作用域,同样无法被外部文件所引用。3.static修饰的指针不能指向auto类型的地址,因为static类型的指针先分配地址。4.static修饰函数使函数具有内部链接,只能在当前文件访问,避免了其他文件中的同名函数冲突。
在c++中:
1.static修饰的成员在文件编译时分配空间,不占用该成员的类对象中的空间,可以不实例化对象直接调用。2.static修饰成员函数时,该成员函数属于类中的,不属于类对象,可以不实例化对象直接通过类名加域限定符进行调用,该静态成员函数不能调用类中的非静态成员变量,只能调用静态成员变量。3.static修饰的成员函数相当于在类中定义了一个全局函数,对类中的同名非静态成员函数不构成函数重载,因为作用域的不同。4.static修饰的成员变量同样属于类,只在类中声明,必须在类外定义,所有类对象共享。未定义的静态成员变量默认值为0。5.static修饰的静态成员变量在被类的实例化对象调用时不占用类的内存空间,相当与在类中定义了一个全局变量,当多个类中都调用了静态成员变量时,相当于对静态成员变量的地址进行操作,一个类对象修改了其的值,其他所有类对象中的静态成员变量都相应改变。6.静态成员变量相对于全局变量,更体现了类的封装性。
面试题2:
在c和c++中const关键字的用法:
在c中:
1.const修饰的全局变量地址在堆区的只读段,不能修改变量的值,但需要定义时初始化。2.const修饰的局部变量值不能改变,地址在栈区。3.const修饰的指针时:当const在*左边时值不能改变,地址能改变。当const在*的右边是地址不能改变,值能改变。当const在*的两侧是地址和值都不能改变。
在c++中:
1.修饰的成员函数是常成员函数,不能修改数据成员。2.修饰的对象是常对象,所有数据成员都不能被改变,成对象只能调用常成员函数。非常对象可以调用常成员函数和非常成员函数,优先非常成员函数。常成员函数和非常成员函数构成函数重载,因为this指针类型不同。3.const修饰的变量可以用mutable关键字取消常属性,让其可以改变。
面试题3:
Qt中基于TCP通信中的服务器和客户端的操作:
服务器:
1.创建一个QTCPServer的类对象,作为一个服务器。
2.对该对象调用listen函数设置监听状态,可以监听指定ip地址,也可以监听所有主机地址,端口号可以指定也可以自动生成。
3.当有客户端发来连接请求时,服务器就会发射一个newConnect信号,我们可以将该信号连接到自定义槽函数进行操作逻辑操作。
4.在槽函数总可以调用nextPendingConnection函数来获取最新连接的客户端套接字的地址,将该套接字存到客户端容器中。
5.此时客户端与服务器已经建立连接请求,如果有客户端向服务器发来数据,客户端就会发射一个readyRead信号。
6.服务器可以用read,readLine,readALL函数来完成数据的读取。
7.向套接字中写数据,可以使用write函数完成。
8.用close函数关闭服务器.
客户端:
1.实例化一个QTCPSocket的类对象。
2.调用该对象的成员函数connectToHost连接到服务器,连接服务器时需要给定服务器的ip地址和端口号
3.如果连接服务器成功,那么该客户端就会自动发射一个connected的信号,我们可以将该信号连接到对应的槽函数中尽显逻辑操作
4.如果服务器有数据向客户端发来,那么客户端就会自动发射一个readyRead信号,我们可以在该信号对应的槽函数中处理数据。
5.可以使用read,readLine,readAll读取客户端套接字中的数据
6.可以使用write向服务器中发送数据
7.使用成员函数disConnectFromHost断开与服务器的连接
8.如果断开成功,套接字就会发射一个disconnected的信号