caffe 人脸特征提取并计算相似度

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


关注我的公众号,分享资源
公众号搜索: 卡本特
扫码关注

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值