这篇博客主要介绍下用C++代码,提取出一张人脸图片的特征向量,这里的网络,是上文中用到了center loss的网络,根据论文里介绍的,我们提取出人脸图片以及该图片的上下翻转图各自经过网络在fc5层输出的特征向量,然后将两者拼起来,形成一个2倍维数的特征向量,该特征向量表征了这个人脸。这样子,我们计算出一对待验证的人脸图片各自的特征向量后,就可以计算相似度了,这里用余弦相似度。
同样的,作为C++爱好者,我还是将代码组织成一个人脸识别类 class FaceRecognition ,该类只有一个共有的接口
float getSimilarity(const Mat& lhs,const Mat& rhs,bool useFlipImg=true); //比较两个图像的余弦相似度
输入两张待识别的人脸图片(注意,这个函数不会对人脸图片进行对齐操作),计算出其余弦相似度并返回。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace caffe; // NOLINT(build/namespaces)
using namespace cv;
using namespace std;
//人脸验证类
class FaceRecognition{ //提取网络最后一层特征
public:
//构造函数,需要一个deploy.prototxt文件和一个caffemodel
FaceRecognition(const string& model_file,const string& trained_file ){
_net.reset(new Net(model_file,TEST)); //定义一个网络
_net->CopyTrainedLayersFrom(trained_file); //加载权重
}
float getSimilarity(const Mat& lhs,const Mat& rhs,bool useFlipImg=true){ //比较两个图像的余弦相似度
//计算余弦相似度
vector feat1,feat2;
if(useFlipImg){
feat1=getLastLayerFeaturesFlip(lhs);
feat2=getLastLayerFeaturesFlip(rhs);
}
else{
feat1=getLastLayerFeatures(lhs);
feat2=getLastLayerFeatures(rhs);
}
return std::max(0,getSimilarity(feat1,feat2));
}
private:
shared_ptr > _net; //CNN网络
float getMold(const vector& vec){ //求向量的模长
int n = vec.size();
float sum = 0.0;
for (int i = 0; i& lhs, const vector& rhs){
int n = lhs.size();
assert(n == rhs.size());
float tmp = 0.0; //内积
for (int i = 0; i getLastLayerFeaturesFlip(const Mat& _img){
vector result1 = getLastLayerFeatures(_img); //原图的特征向量,需要再计算翻转图的特征向量
Mat flipImg;
flip(_img, flipImg, 0); //上下翻转
vector result2 = getLastLayerFeatures(flipImg);
for (int i = 0; i getLastLayerFeatures(const Mat& _img){ //求一张图片经过最后一层(fc5)的特征向量
Mat img = _img.clone();
img.convertTo(img, CV_32FC3); //转为浮点图
Blob* inputBlob = _net->input_blobs()[0];
int width = inputBlob->width(); //网络规定的输入图片的宽度和高度
int height = inputBlob->height();
resize(img, img, Size(width, height)); //将测试图片进行调整大小
img = (img - 127.5)*0.0078125; //减均值,再缩放到-1 到 1
float* data = inputBlob->mutable_cpu_data(); //将图片的像素值,复制进网络的输入Blob
for (int k = 0; k<3; ++k){
for (int i = 0; i(i, j)[k];
}
}
}
vector* > inputs(1, inputBlob);
const vector* >& outputBlobs = _net->Forward(inputs); //进行前向传播,并返回最后一层的blob
Blob* outputBlob = outputBlobs[0]; //输出blob
const float* value = outputBlob->cpu_data();
vector result;
for (int i = 0; icount(); ++i) //将输出blob里的特征数值,拷贝到vector里并返回
result.push_back(value[i]);
return result;
}
};
代码进行了大量的注释,且大量中间过程,都在私有函数里,最终直接调用getSimilarity函数就能得到两张图片的相似度。
还有一点需要注意的是,因为代码中,强调的是输出最后一层的输出值,所以我们的deploy.prototxt文件,要把softmax层以及accuracy层什么的都注释掉,直到最后一个我们想输出的层,比如我们这里是要得到fc5的输出值,则要把fc5层的后面的都注释掉。具体文件可以参看作者的GitHub。
https://github.com/ydwen/caffe-face
部分效果如下
我们这里使用的人脸检测以及人脸对齐的方法是上上篇博客中的方法,MTCNN,论文是
Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Networks
然后,训练人脸图片用到神经网络以及方法(center loss)以及GitHub分别是
ECCV16 《A Discriminative Feature Learning Approach for Deep Face Recognition》
https://github.com/ydwen/caffe-face