失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > QT OpenCV人脸考勤识别

QT OpenCV人脸考勤识别

时间:2022-07-02 19:16:18

相关推荐

QT OpenCV人脸考勤识别

前言

突发奇想 要做一个人脸考勤系统

我编译的是最新的OpenCV3.4.17的库 这玩意编译了一晚上

这玩意我也很想写一个 Mingw32 OpenCV的编译

但是这个有可能我没报错的地方你们报错了 所以这个就不写了

一、项目介绍

Task OpenCV摄像头的线程部分

Gui 界面

Sql 数据库

Pub 公共类 本来是想把样式表跟一些共有放在里面 后面太懒了

Face 人脸训练跟写入文件的类

Filter 与摄像头一起的类 线程的一些行为会在里面执行

Model 代理模型空间 View Model Delegate

FaceInfo dll 跟一些存放人脸识别的数据 与代码本身无关

接下来还是挑一些重点的写一下

//Gui肯定不讲 Gui每个人的编写风格不一样

//Model 也不讲 因为之前的文章讲过

//Pub 里面也是空的

其他的挑点重点的写写

二、Task

1. 打开摄像头跟线程移动

//我不喜欢用双斜杠写 因为那个颜色真的太暗了//打开摄像头//AFilter::Get()->Add(Type_Voice);这个晚点说 先无视这里bool TaskCamera::Open(){_mux.lock();const bool isOpen = _cvCamera.open(0);_mux.unlock();AFilter::Get()->Add(Type_Voice);if(!isOpen){qDebug() << "Open Error";return false;}return true;}//线程跑void TaskCamera::run(){bool isRead = false;int trainPaintCount = 0;int InfoClear = 0;while(1){_mux.lock();//退出if(_isClose){_mux.unlock();break;}//摄像头没打开if(!_cvCamera.isOpened()){_mux.unlock();continue;}//读取摄像头一帧isRead = _cvCamera.read(_cvCameraPaint);//读取失败 或图片为空if(!isRead || _cvCameraPaint.empty()){_mux.unlock();continue;}//是否是训练图片模式if(_isTrain){//训练图片的逻辑是//训练一张 写入这张图片 并且写入配置文件if(trainPaintCount >= 10){_isTrain = false;AFilter::Get()->Clear();AFilter::Get()->Add(Type_Voice);_nCountLabel += 1;_pCountLabel += trainPaintCount;emit PaintWriteFinish(_pCountLabel,_nCountLabel);trainPaintCount = 0;}QList<QVariant> list;list.push_back(trainPaintCount + _pCountLabel);list.push_back(_nCountLabel);AFilter::Get()->SetValue(list);GetReturnData data = AFilter::Get()->DoFilter(_cvCameraPaint);_cvCameraPaint = data.mat;if(data.value.toBool()){trainPaintCount++;}}else{//如果没有训练 就将图片开始进行识别GetReturnData data = AFilter::Get()->DoFilter(_cvCameraPaint);if(data.value != -1){//如果有人脸进行了认证InfoClear = 0;emit VoiceIdentify(data.value.toInt());}_cvCameraPaint = data.mat;}//显示给QOpenGLWidgetemit UpdateMat(_cvCameraPaint);Sleep(32);InfoClear++;//如果40帧没有人脸识别就清空界面上的信息if(InfoClear >= 40){InfoClear = 0;emit VoiceIdentify(-1);}_mux.unlock();}}我不用双斜线写 因为太暗了先说一下这里的参数 你知道的 看别人的代码想知道别人的想法很难但是我尽可能的表达清晰一些首先是 AFilter 这个隶属于Filter的文件夹 是一个处理图片的类后面也会讲 先防止看到这里比较懵逼这个参数是为了检查是不是有10张图片丢进里面去训练int trainPaintCount = 0;这个是为了检查40帧是不是有人脸录入 没有就清空界面上的一些文字int InfoClear = 0;

三 Filter

1.AFilter

enum FilterType{//检测Type_Voice,//训练Type_WritePaint,};struct GetReturnData{cv::Mat mat;QVariant value;};class AFilter{public://单例模式static AFilter* Get();//添加模式virtual void Add(FilterType) = 0;//设置值virtual void SetValue(QList<QVariant>) = 0;//清空virtual void Clear() = 0;//具体操作virtual GetReturnData DoFilter(cv::Mat,cv::Mat = cv::Mat()) = 0;protected:AFilter();virtual ~AFilter();};AFilter *AFilter::Get(){static XFilter cx;return &cx;}

2.XFilter

class XFilter : public AFilter{public:virtual void Add(FilterType);virtual void Clear();virtual GetReturnData DoFilter(cv::Mat,cv::Mat = cv::Mat());virtual void SetValue(QList<QVariant>);XFilter();virtual ~XFilter();signals:void CameraIdentify();private:QList<QVariant> _valueList;QList<FilterType> _taskList;FilterAction _action;};//最重要的就是这个 根据模式 选择不一样的操作GetReturnData XFilter::DoFilter(cv::Mat mat1, cv::Mat mat2){GetReturnData data;_action.SetMat(mat1,mat2);for(auto& i : _taskList){switch (i){//检测模式case Type_Voice:{const int PCANum = _action.VoiceAction();data.value = PCANum;break;}//训练模式case Type_WritePaint:{const int pCountNum = _valueList.at(0).toInt();const int nCountNum = _valueList.at(1).toInt();const bool isWrite = _action.WritePaintAction(pCountNum,nCountNum);data.value = isWrite;break;}}}data.mat = _action.GetMat();return data;}训练模式这里需要传入值这个值就是图片的名称因为后续训练需要用上一个cv::Mat防止名称相同所以需要用上一个索引 1.jpg 2.jpg等第二个就是一个int 比如告诉你这个图片代表训练的是第几个人脸

三 FilterAction

1.一些声明

//OpenCV的2个对象 //cv::CascadeClassifier是打开人脸用的 //cv::Ptr<cv::face::BasicFaceRecognizer>就是打开自己的检测文件//逻辑是先用人脸识别识别到人脸 然后使用自己的检测文件去检测是谁cv::CascadeClassifier _cascade;cv::Ptr<cv::face::BasicFaceRecognizer> _modelPCA;_cascade.load(FaceCascadePath.toStdString());_modelPCA = cv::face::EigenFaceRecognizer::create();_modelPCA->read(FaceModelPath.toStdString())

2.检测

int FilterAction::VoiceAction(){int PCANum = -1;if(!_inputMat.empty()){//为什么要resize 因为太卡了 电脑垃圾是这样的cv::resize(_inputMat,_inputMat,cv::Size(300,250));cv::Mat matGary;//一定要转成灰度图训练cv::cvtColor(_inputMat,matGary,cv::COLOR_BGR2GRAY);std::vector<cv::Rect> faceVec;//人脸识别_cascade.detectMultiScale(matGary,faceVec,1.1,3,0 | CV_HAAR_DO_ROUGH_SEARCH);//只有1张人脸的时候 这个描述感觉怪怪的-.-if(faceVec.size() == 1){//获取人脸的Rect信息cv::Rect faceInfo = faceVec.at(0);//在灰度图中选出这个信息cv::Mat trainMat = matGary(faceInfo);//将图片转成92 112 这个92 112好像是opencv人脸库就是这个值cv::resize(trainMat,trainMat,cv::Size(92,112));//图片放进去训练int numPCA = _modelPCA->predict(trainMat);qDebug() <<numPCA;//因为这个准确度不够 所以我希望连续监测10次是这个人才确认打卡成功if(_isSeleteCount == 0){_isSelete = numPCA;_isSeleteCount++;}else if(_isSeleteCount >= 0 && _isSeleteCount <= 9){if(_isSelete == numPCA){_isSeleteCount++;}else{_isSeleteCount = 0;}}else if(_isSeleteCount == 10){_isSeleteCount = 0;PCANum =_isSelete;}//框出人脸cv::rectangle(_inputMat,faceInfo.tl(),faceInfo.br(),cv::Scalar(255,0,255),2);}}return PCANum;}

2.训练

bool FilterAction::WritePaintAction(int pCountNum,int nCountNum){//刚刚说的 1.jpg 跟 int 是第几个人的脸const QString paintPath = PaintInfoPath + QString::number(pCountNum) + ".jpg";const QString writePath = paintPath + ";" + QString::number(nCountNum);if(!_inputMat.empty()){cv::Mat matGary;//怕太卡了cv::resize(_inputMat,_inputMat,cv::Size(300,250));//转灰度cv::cvtColor(_inputMat,matGary,cv::COLOR_BGR2GRAY);std::vector<cv::Rect> faceVec;//人脸识别_cascade.detectMultiScale(matGary, faceVec, 1.1, 3, 0 | CV_HAAR_DO_ROUGH_SEARCH);if(faceVec.size() == 1){cv::Rect faceInfo = faceVec.at(0);//只有一张脸的情况cv::Mat faceMat = matGary(faceInfo);if(faceMat.cols <= 0 || faceMat.rows <= 0)return false;//OPencv人脸文件夹的大小cv::resize(faceMat,faceMat,cv::Size(92,112));const bool isWrite = cv::imwrite(paintPath.toStdString(),faceMat);//框出人脸cv::rectangle(_inputMat,faceInfo.tl(),faceInfo.br(),cv::Scalar(0,255,0),2);if(!isWrite){return false;}QFile file(TrainInfoPath);const bool isOpen = file.open(QFile::ReadWrite | QFile::Text | QFile::Append);if(!isOpen){return false;}//将图片写入这个文件夹file.write(writePath.toStdString().data());file.write("\n");file.flush();file.close();}}return true;}

这边需要联动一下Face文件夹里面的类因为这上面的训练是为了写入一张回复是否写入成功一张

如果确确实实写入了10张图片 才开始去训练自己的模型

void FaceModelTrain::Train(int pCount,int nCount){//将现在的一个总数读出来//比如我现在文件里面的信息会是 40:4读出这个40跟4//将40:4更新成50:5 方便下次训练的时候读这个文件可以有一个序号const QString TrainCountWrite = QString::number(pCount) + ":" + QString::number(nCount);QFile fileCount(FaceCountPath);bool isOpen = fileCount.open(QFile::WriteOnly | QFile::Text);if(!isOpen){return;}bool isWrite = fileCount.write(TrainCountWrite.toStdString().data());if(!isWrite){return;}std::vector<cv::Mat> imageVec;std::vector<int> labelVec;//打开训练文件夹//刚刚上一步是为了保存Mat 跟 int//打个比方 就是读取一行//xxxxxx/1.jpg;0//就是在这个里面读取1.jpg跟他的一个索引//然后cv::imread 跟 读索引 拿图片跟序列号做图片QFile file(trainInfoPath);isOpen = file.open(QIODevice::ReadOnly | QIODevice::Text);if(isOpen){QString line;QTextStream in(&file); //用文件构造流line = in.readLine();//读取一行放到字符串里while(!line.isNull())//字符串有内容{QStringList msg = line.split(";");imageVec.push_back(cv::imread(msg.at(0).toStdString(),CV_LOAD_IMAGE_GRAYSCALE));labelVec.push_back(msg.at(1).toInt());line=in.readLine();//循环读取下行}}//判断 读出来的图片跟索引不一致可能会at的时候报错if(imageVec.size() != labelVec.size()){return;}const unsigned int imageCount = imageVec.size();if(imageCount <= 0){return;}//具体的训练模型 有三个 自己百度把 反正我也不懂 恩用的cv::Ptr<cv::face::BasicFaceRecognizer> model = cv::face::EigenFaceRecognizer::create();model->train(imageVec, labelVec);qDebug() << PCAPath;model->save(PCAPath.toStdString());cv::Ptr<cv::face::BasicFaceRecognizer> model1 = cv::face::FisherFaceRecognizer::create();model1->train(imageVec, labelVec);qDebug() << FisherPath;model1->save(FisherPath.toStdString());cv::Ptr<cv::face::LBPHFaceRecognizer> model2 = cv::face::LBPHFaceRecognizer::create();model2->train(imageVec, labelVec);qDebug() << LBPHPath;model2->save(LBPHPath.toStdString());emit TrainFinish(nCount);}

四 SqlDataBase

这里使用的是QSQLITE数据库 不是mysql mysql自己配置 以前我也用过

以下的都是简单的增删改查 会的可以直接无视

1.打开数据库

bool SqlDataBase::Open(){bool isOpen = false;if (QSqlDatabase::contains("qt_sql_default_connection")){_db = QSqlDatabase::database("qt_sql_default_connection");}else{_db = QSqlDatabase::addDatabase("QSQLITE");}isOpen = OpenDataBase();return isOpen;}

2.插入文件

bool SqlDataBase::InsertFaceClock(QVariantList valueList){_db.setDatabaseName(DataBaseFilePath);if(!_db.open())return false;const QString name = valueList.at(0).toString();const int id = valueList.at(1).toInt();const QString dateTime = valueList.at(2).toString();const QString insertStr = QString("insert into FaceDataBase values(%1,'%2',Datetime('%3'))").arg(id).arg(name).arg(dateTime);qDebug() << insertStr;QSqlQuery query(_db);bool isExec = query.exec(insertStr);if(isExec){return false;}_db.close();return true;}

3.读取全部数据库所有信息

bool SqlDataBase::GetFaceClockAllData(QList<QStringList> & List){_db.setDatabaseName(DataBaseFilePath);if(!_db.open())return false;const QString SelectStr = QString("select * from FaceDataBase");QSqlQuery query(_db);const bool isSelect = query.exec(SelectStr);if(!isSelect)return false;while(query.next()){QStringList sqlList;for(int i = 0; i <= 2; i++){sqlList.push_back(query.value(i).toString());}List.push_back(sqlList);}_db.close();return true;}

4.条件查询

bool SqlDataBase::GetClockData(QList<QStringList>& List, QString name, QString id, QString DateTime1, QString DateTime2){_db.setDatabaseName(DataBaseFilePath);if(!_db.open())return false;if(name.isEmpty() && id.isEmpty() && DateTime1.isEmpty() && DateTime2.isEmpty())return false;if(!name.isEmpty() && id.isEmpty() && DateTime1.isEmpty() && DateTime2.isEmpty()){const QString selectStr = QString("select * from FaceDataBase where name = '%1'").arg(name);bool isSelect = GetValue(List,selectStr);if(!isSelect)return false;}else if(name.isEmpty() && !id.isEmpty() && DateTime1.isEmpty() && DateTime2.isEmpty()){const QString selectStr = QString("select * from FaceDataBase where id = %1").arg(id);bool isSelect = GetValue(List,selectStr);if(!isSelect)return false;}else if(name.isEmpty() && id.isEmpty() && !DateTime1.isEmpty() && !DateTime2.isEmpty()){const QString selectStr = QString("select * from FaceDataBase where ClockTime >= Datetime('%1') and ClockTime <= Datetime('%2')").arg(DateTime1).arg(DateTime2);bool isSelect = GetValue(List,selectStr);if(!isSelect)return false;}else if(!name.isEmpty() && id.isEmpty() && !DateTime1.isEmpty() && !DateTime2.isEmpty()){const QString selectStr = QString("select * from FaceDataBase where name = '%1' and ClockTime >= Datetime('%2') and ClockTime <= Datetime('%3')").arg(name).arg(DateTime1).arg(DateTime2);bool isSelect = GetValue(List,selectStr);if(!isSelect)return false;}else if(name.isEmpty() && !id.isEmpty() && !DateTime1.isEmpty() && !DateTime2.isEmpty()){const QString selectStr = QString("select * from FaceDataBase where id = %1 and ClockTime >= Datetime('%2') and ClockTime <= Datetime('%3')").arg(id).arg(DateTime1).arg(DateTime2);bool isSelect = GetValue(List,selectStr);if(!isSelect)return false;}else if(name.isEmpty() && id.isEmpty() && !DateTime1.isEmpty() && !DateTime2.isEmpty()){const QString selectStr = QString("select * from FaceDataBase where name = '%1' and id = %2 and ClockTime >= Datetime('%3') and ClockTime <= Datetime('%4')").arg(name).arg(id).arg(DateTime1).arg(DateTime2);bool isSelect = GetValue(List,selectStr);if(!isSelect)return false;}_db.close();return true;}bool SqlDataBase::GetValue(QList<QStringList>& List, QString selectStr){QSqlQuery query(_db);const bool isSelect = query.exec(selectStr);if(!isSelect)return false;while(query.next()){QStringList sqlList;for(int i = 0; i <= 2; i++){sqlList.push_back(query.value(i).toString());}List.push_back(sqlList);}return true;}

如果觉得《QT OpenCV人脸考勤识别》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。