热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Qt+Caffe+OpenCV——【一个基于VGG网络的人脸识别考勤系统】(一)人脸检测与识别模块

前言本篇为Qt+Caffe+OpenCV——【一个基于VGG网络的人脸识别考勤系统】的第二篇博文,将所有的人脸检测与识别进行实现。与原博文相比,本文的人脸检测与识别更为简洁,少了人脸矫

前言

本篇为Qt+Caffe+OpenCV——【一个基于VGG网络的人脸识别考勤系统】的第二篇博文,将所有的人脸检测与识别进行实现。与原博文相比,本文的人脸检测与识别更为简洁,少了人脸矫正模块,放弃了dlib的使用,对系统影响不大。

环境

采用的是Visual Studio2013 + Qt 5.7的VS开发控件。
请确保你已经按照http://blog.csdn.net/mr_curry/article/details/52443126配置好caffe。
libfacedetection库:https://github.com/ShiqiYu/libfacedetection

代码

姑且将整个系统的名字命名为YotoFace。
人脸的数据由SingleFace类来完成,其具备的是存储人脸的信息。
SingleFace.h:

#pragma once
#include
using namespace cv;
using namespace std;

class SingleFace
{
public:
string label;//人的名字
Mat sourceImage;//原图
Mat Roi_224;//截取人脸后224*224的图像
Rect position;//其在原图中的位置
vector<float> feature;//人脸ROI提取出的向量

bool empty() //根据224*224图像判断类是否为空
{
if (Roi_224.empty())
return true;
else
return false;
}

void draw()//画画
{
rectangle(sourceImage, position, Scalar(0, 0, 255));
}
private:
};

人脸检测和识别都由YotoFace类来完成。
YotoFace.h:

#pragma once
#include
#include
#include
#include "caffe/layers/input_layer.hpp"
#include "caffe/layers/inner_product_layer.hpp"
#include "caffe/layers/dropout_layer.hpp"
#include "caffe/layers/conv_layer.hpp"
#include "caffe/layers/relu_layer.hpp"
#include
#include "caffe/layers/pooling_layer.hpp"
#include "caffe/layers/lrn_layer.hpp"
#include "caffe/layers/softmax_layer.hpp"
#include
#include

class YotoFace
{
public:
YotoFace();
vector<float> ExtractFeature(Mat input_224);//提取特征
bool Generate(Mat input, SingleFace &singleface);//没有label的情况下进行的Generate,表示的是识别
bool Generate(Mat input, SingleFace &singleface, string label);//有label的情况下进行的Generate,表示的是注册
SingleFace Recognition(Mat input_224, SingleFace &singleface);//输入一个224*224的图片,查找他属于哪个人
vector FaceArray;//众多的SingleFace

//仅仅用于人脸检测
void drawFaceImage(Mat input);//多线程有用
private:
caffe::MemoryDataLayer<float> *memory_layer;//进行数据输入的层
caffe::Net<float>* net;//整个layer和权重
bool FaceDetect(Mat input, Rect &roi);//人脸检测的接口
mutex thread_mutex;//线程锁
};

YotoFace.cpp:

#include 
#include

namespace caffe
{
extern INSTANTIATE_CLASS(InputLayer);
extern INSTANTIATE_CLASS(InnerProductLayer);
extern INSTANTIATE_CLASS(DropoutLayer);
extern INSTANTIATE_CLASS(ConvolutionLayer);
REGISTER_LAYER_CLASS(Convolution);
extern INSTANTIATE_CLASS(ReLULayer);
REGISTER_LAYER_CLASS(ReLU);
extern INSTANTIATE_CLASS(PoolingLayer);
REGISTER_LAYER_CLASS(Pooling);
extern INSTANTIATE_CLASS(LRNLayer);
REGISTER_LAYER_CLASS(LRN);
extern INSTANTIATE_CLASS(SoftmaxLayer);
REGISTER_LAYER_CLASS(Softmax);
extern INSTANTIATE_CLASS(MemoryDataLayer);
}

//构造
YotoFace::YotoFace()
{
net = new caffe::Net<float>("vgg_extract_feature_memorydata.prototxt", caffe::TEST);
net->CopyTrainedLayersFrom("VGG_FACE.caffemodel");
memory_layer = (caffe::MemoryDataLayer<float> *)net->layers()[0].get();
}

//提取特征
vector<float> YotoFace::ExtractFeature(Mat img_224) //ensure input 224*224!!!
{
std::vector test{ img_224 };
std::vector<int> testLabel{ 0 };
memory_layer->AddMatVector(test, testLabel);// memory_layer and net , must be define be a global variable.
vectorfloat>*> input_vec;
net->Forward(input_vec);
auto fc7 = net->blob_by_name("fc7");//提取fc7层!4096维特征
float* begin = fc7->mutable_cpu_data();
vector<float> feature{ begin, begin + fc7->channels() };
//cout <channels();
return move(feature);
}

//私有的人脸检测函数
bool YotoFace::FaceDetect(Mat input, Rect &roi)
{
thread_mutex.lock();
{
Mat gray;
cvtColor(input, gray, CV_BGR2GRAY);
int * pResults = NULL;
pResults = facedetect_multiview_reinforce((unsigned char*)(gray.ptr(0)), gray.cols, gray.rows, gray.step,
1.2f, 5, 24);
//dlib.
//
int p_num = (pResults ? *pResults : 0);
if (p_num == 0)
{
thread_mutex.unlock();
return false;
}

short * p = ((short*)(pResults + 1));
Point left(p[0], p[1]);
Point right(p[0] + p[2], p[1] + p[3]);
roi = Rect(left, right);
thread_mutex.unlock();
return true;
}
}

//单张图片生成SingleFace
bool YotoFace::Generate(Mat input, SingleFace &singleface)
{
Rect roi;
if (FaceDetect(input, roi))
{
Mat img_224 = input(roi);
resize(img_224, img_224, Size(224, 224));
auto feature_=ExtractFeature(img_224);

if (feature_.empty())
return false;
else
{
singleface.sourceImage = input;
singleface.position = roi;
singleface.feature = feature_;
singleface.Roi_224 = img_224;
return true;
}
}
else
{
return false;
}
}

//有标签的数据
bool YotoFace::Generate(Mat input, SingleFace &singleface,string label_)
{
Rect roi;
if (label_.empty())
return false;
if (FaceDetect(input, roi))
{
Mat img_224 = input(roi);
resize(img_224, img_224, Size(224, 224));
auto feature_ = ExtractFeature(img_224);

if (feature_.empty())
return false;
else
{
singleface.sourceImage = input;
singleface.position = roi;
singleface.feature = feature_;
singleface.Roi_224 = img_224;
singleface.label = label_;
return true;
}
}
else
{
return false;
}
}

void YotoFace::drawFaceImage(Mat input)
{
Rect rec;
if (FaceDetect(input, rec))
{
//有人脸
Mat draw = input;
rectangle(draw, rec, Scalar(0, 0, 255), 2);
}

}

这里解释一下YotoFace::FaceDetect这个函数需要加锁的原因。在具体落实到Qt上的时候,我们应该是希望有一个窗口是始终在检测人脸的,这就需要死循环,若不能跳出则会影响其他程序的执行。所以要采用多线程。而如果我们用的是libfacedetection,在两条线程同时调用函数时将会出错,所以要保证同时段只能调用一次。
thread_mutex.lock()表示锁上当前的线程,当别的线程碰到它时,会处于挂起状态,等待唤醒。
thread_mutex.unlock()进行解锁。
问题来了。人脸也检测了,特征也提取了,如何计算向量距离?
LikeValue的实现是?
ComputeDistance.cpp:

#include 

inline double LikeValue(float *v1, float *v2, int channels)
{
//计算内积:
double mult = 0;
double v1_2 = 0;
double v2_2 = 0;
for (int i = 0; i {
mult += v1[i] * v2[i];
v1_2 += pow(v1[i], 2);
v2_2 += pow(v2[i], 2);
}

return mult / (sqrt(v1_2)*sqrt(v2_2));
}


SingleFace YotoFace::Recognition(Mat input_, SingleFace &singleface)
{
//解析:
if (Generate(input_, singleface))
{
float *single_feature = &singleface.feature[0];
int single_channel = singleface.feature.size();

int size_ = FaceArray.size();//有多少个人脸需要对比的
vector<double> like_array;
for (int i = 0; i {
float *faces_feature = &FaceArray[i].feature[0];
like_array.push_back(LikeValue(single_feature, faces_feature, single_channel));
}

vector<double>::iterator biggest = std::max_element(std::begin(like_array), std::end(like_array));
int max_ = distance(std::begin(like_array), biggest);

return FaceArray[max_];

}

else
{
return singleface;
}

}

转成数组运算,速度更快。

实际上在应用时,在未按注册按钮时,A窗口是通过第2个线程调用的drawFaceImage():
这里写图片描述
按下确认按钮的一瞬间,主线程也会调用drawFaceImage()中的FaceDetect(),由于加了锁,所以很安全啦。
这里写图片描述
识别的时候,调用的是Recognition()函数,也有锁保护。
这里写图片描述

结语

无GPU环境,速度杠杠的。


推荐阅读
author-avatar
萝莉吴文娟
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有