caffe 人脸特征提取并计算相似度
背景
要做人脸识别相关任务,查了很多资料,最近比较火的center-loss做人脸识别。利用caffe-face的模型来提取人脸特征,由于一直都没有深入到caffe的api来做过,一直都在到处查资料。现在将过程记录下。
caffe-face
caffe-facehttps://github.com/ydwen/caffe-face源码相较于caffe源码里面,多了一个face_example的文件夹,下面有一个extractDeepFeature.m文件,但是是matlab的文件,需要将其转换为c的。小白一枚,所以一步一步的“翻译”。
matlab代码
clear;clc;
addpath('path_to_matCaffe/matlab');
caffe.reset_all();
% load face model and creat network
caffe.set_device(0);
caffe.set_mode_gpu();
model = 'path_to_deploy/face_deploy.prototxt';
weights = 'path_to_model/face_model.caffemodel';
net = caffe.Net(model, weights, 'test');
% load face image, and align to 112 X 96
imgSize = [112, 96];
coord5points = [30.2946, 65.5318, 48.0252, 33.5493, 62.7299; ...
51.6963, 51.5014, 71.7366, 92.3655, 92.2041];
image = imread('./caffe-face-caffe-face/face_example/Jennifer_Aniston_0016.jpg');
facial5points = [105.8306, 147.9323, 121.3533, 106.1169, 144.3622; ...
109.8005, 112.5533, 139.1172, 155.6359, 156.3451];
Tfm = cp2tform(facial5points', coord5points', 'similarity');
cropImg = imtransform(image, Tfm, 'XData', [1 imgSize(2)],...
'YData', [1 imgSize(1)], 'Size', imgSize);
% transform image, obtaining the original face and the horizontally flipped one
if size(cropImg, 3) < 3
cropImg(:,:,2) = cropImg(:,:,1);
cropImg(:,:,3) = cropImg(:,:,1);
end
cropImg = single(cropImg);
cropImg = (cropImg - 127.5)/128;
cropImg = permute(cropImg, [2,1,3]);
cropImg = cropImg(:,:,[3,2,1]);
cropImg_(:,:,1) = flipud(cropImg(:,:,1));
cropImg_(:,:,2) = flipud(cropImg(:,:,2));
cropImg_(:,:,3) = flipud(cropImg(:,:,3));
% extract deep feature
res = net.forward({cropImg});
res_ = net.forward({cropImg_});
deepfeature = [res{1}; res_{1}];
caffe.reset_all();
现在的目标是根据这个matlab的代码,将其写成C++的代码。具体的流程如下:
①加载模型,初始化网络
caffe_model和prototxt,两个文件。指定CPU或者GPU模式。
boost::shared_ptr<Net<float> > _net;
string trained_file = "./mobile-id-master/models/mobile_id.caffemodel";
string model_file = "./mobile-id-master/models/mobile_id_gallery(change.prototxt";
Caffe::set_mode(Caffe::GPU);
_net.reset(new Net<float>(model_file,TEST)); //定义一个网络
_net->CopyTrainedLayersFrom(trained_file); //加载权重
②人脸对齐
因为caffe-face给的训练模型中的人脸大小是112x96,所以需要将输入的人脸图片变成112X96。然后是人脸对齐,需要一张标准脸的五个特征点的位置坐标,将待提取特征的人脸进行对齐处理。这里面facial5points的五个点的坐标是人脸检测(MTCNN)部分得到的五点位置信息。对于matlab里面的这两个函数,我在opencv里面找到的替代,不知道其具体底层实现是不是一样的,cp2tform和imtransform。将对齐部分写了一个函数。
cv::Mat alignFace(cv::Mat img, cv::Rect rects, std::vector<cv::Point2d>& points)
{
std::vector<cv::Point2d> targrtCoordinate;
targrtCoordinate.push_back(cv::Point2d(30.2946, 51.6963));
targrtCoordinate.push_back(cv::Point2d(65.5318, 51.5014));
targrtCoordinate.push_back(cv::Point2d(48.0252, 71.7366));
targrtCoordinate.push_back(cv::Point2d(33.5493, 92.3655));
targrtCoordinate.push_back(cv::Point2d(62.7299, 92.2041));
cv::Mat alignImg;
cv::Mat transform = cv::estimateRigidTransform(points, targrtCoordinate, 0);
if (((transform.type() == CV_32F || transform.type() == CV_64F) && transform.rows == 2 && transform.cols == 3))
{
//必须要能正确的进行刚体转换才行
cv::warpAffine(img, alignImg, transform, cv::Mat::zeros(112, 96, img.type()).size());
}
else
{
//直接根据检测坐标进行裁切
cv::Mat crop = img(cv::Rect(rects.x, rects.y, rects.width, rects.height));
cv::Mat tmp;
cv::resize(crop, tmp, cv::Size(96, 112), 0, 0, CV_INTER_LINEAR);
}
return alignImg;
}
③前向传播
matlab中前向传播,直接net.forward返回值就是特征向量了。在c++的接口中不是这样的。
vector<float> getLastLayerFeatures(const Mat& _img){ //求一张图片经过最后一层的特征向量
Mat img = _img.clone();
img.convertTo(img, CV_32FC3); //转为浮点图
Blob<float>* 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<height; ++i){
for (int j = 0; j<width; ++j){
int index = (k*height + i)*width + j; //获取偏移量
data[index] = img.at<Vec3f>(i, j)[k];
}
}
}
vector<Blob<float>* > inputs(1, inputBlob);
const vector<Blob<float>* >& outputBlobs = _net->Forward(inputs); //进行前向传播,并返回最后一层的blob
Blob<float>* outputBlob = outputBlobs[0]; //输出blob
const float* value = outputBlob->cpu_data();
vector<float> result;
for (int i = 0; i<outputBlob->count(); ++i) //将输出blob里的特征数值,拷贝到vector里并返回
result.push_back(value[i]);
return result;
}
以上是直接根据prototxt里面的网络结构,获取最后一层的输出特征向量。如果不是获取最后一层的输出,可以根据prototxt里面的层的名字获取特征向量。
_net->Forward();
boost::shared_ptr<caffe::Blob<float>> fc5 = _net->blob_by_name("fc5");//提取fc5层
const float* psfc5 = fc5->cpu_data();
std::vector<float> result;//提取特征的容器
for (int i = 0;i < fc5->count();i++){
result.push_back(*psfc5);
psfc5++;
}
特征余弦相似度
float getSimilarity(const vector<float>& lhs, const vector<float>& rhs){
int n = lhs.size();
assert(n == rhs.size());
float tmp = 0.0; //内积
for (int i = 0; i<n; ++i)
tmp += lhs[i] * rhs[i];
return tmp / (getMold(lhs)*getMold(rhs));
}
float getMold(const vector<float>& vec){ //求向量的模长
int n = vec.size();
float sum = 0.0;
for (int i = 0; i<n; ++i)
sum += vec[i] * vec[i];
return sqrt(sum);
}
然后
ENJOY IT
关注我的公众号,分享资源
公众号搜索: 卡本特
扫码关注