Caffe-Face 特征提取C++版本
说明:
根据extractDeepFeature.m修改的C++版本
extractDeepFeature.cpp:
#include <string>
#include <vector>
#include <fstream>
#include <caffe/caffe.hpp>
#include <caffe/layers/memory_data_layer.hpp>
#include <opencv2/opencv.hpp>
#include "boost/make_shared.hpp"
#define NetF float
#define USING_GPU //使用GPU
#define IMAGE_WIDTH_STD 96//裁剪的图片大小
#define IMAGE_HEIGHT_STD 112
#define LANDMARK_SIZE 5//对齐点的个数
#define LANDMARK_SIZE_DOUBLE 10//对齐点个数的两倍
using namespace caffe;
using namespace std;
using namespace cv;
double src_landmark[LANDMARK_SIZE_DOUBLE] = {
105.8306, 147.9323, 121.3533, 106.1169, 144.3622,
109.8005, 112.5533, 139.1172, 155.6359, 156.3451 };
double dst_landmark[LANDMARK_SIZE_DOUBLE] = {
30.2946, 65.5318, 48.0252, 33.5493, 62.7299,
51.6963, 51.5014, 71.7366, 92.3655, 92.2041 };
template <typename Dtype>
caffe::Net<Dtype>* Net_Init_Load(
std::string param_file, std::string pretrained_param_file, caffe::Phase phase)
{
caffe::Net<Dtype>* net(new caffe::Net<Dtype>(param_file, phase));
net->CopyTrainedLayersFrom(pretrained_param_file);
return net;
}
int main(int argc,char **argv)
{
::google::InitGoogleLogging(argv[0]);
#ifdef USING_GPU
Caffe::set_mode(Caffe::GPU);
#else
Caffe::set_mode(Caffe::CPU);
#endif
//加载配置和模型文件
caffe::Net<NetF>* _net = Net_Init_Load<NetF>("../model/face_deploy.prototxt",
"../model/face_model.caffemodel", caffe::TEST);
vector<Point2f> facial5points;
vector<Point2f> coord5points;
for (int i = 0; i < LANDMARK_SIZE; i++)
{
facial5points.push_back(Point2f(src_landmark[i], src_landmark[i + LANDMARK_SIZE]));
coord5points.push_back(Point2f(dst_landmark[i], dst_landmark[i + LANDMARK_SIZE]));
}
Mat image, warp_mat, cropImg, cropImg_;
image = imread("../data/1.jpg",1);
imshow("frame", image);//显示原图片
warp_mat = estimateRigidTransform(facial5points, coord5points, false);
if (warp_mat.empty())
{
std::cout << "NULL" << std::endl;
return 0;
}
cropImg = Mat::zeros(IMAGE_HEIGHT_STD, IMAGE_WIDTH_STD, image.type());
warpAffine(image, cropImg, warp_mat, cropImg.size());//裁剪图片
cropImg.convertTo(cropImg, CV_32F);
cropImg = (cropImg - 127.5) / 128;//均值归一化图片
imshow("normalization", cropImg);
/*
transpose(cropImg, cropImg);//转置图片(matlab与C++图片存储方式不同不需要转置)
Mat temp;//matlab与C++彩色三通道的顺序不同,matlab为RGB,C++为BGR。matlab中需要在提特征时调整顺序,C++则不需要。
vector<Mat> channels;
split(cropImg, channels); //通道的拆分
temp = channels.at(0);
channels.at(0) = channels.at(2);
channels.at(2) = temp;
merge(channels, cropImg);//拆分通道的合并
imshow("cropImg", cropImg);
*/
flip(cropImg, cropImg_, 0);
imshow("cropImg_", cropImg_);//镜像翻转
cropImg.convertTo(cropImg, CV_8U); //这个地方可以考虑使用Datum类型来传递float类型的图片
cropImg_.convertTo(cropImg_, CV_8U);
std::vector<cv::Mat> dv = { cropImg,cropImg_ };//对齐后图片,这里为两张图片需要修改face_deploy.prototxt中data层batch_size:2
std::vector<int> label = { 0,0 };//图片标签(可随便写)
caffe::MemoryDataLayer<NetF> *m_layer_ = (caffe::MemoryDataLayer<NetF> *)_net->layers()[0].get();//定义数据层指针
m_layer_->AddMatVector(dv, label);//将图片和标签添加到MemoryData层
int end_ind = _net->layers().size();
std::vector<caffe::Blob<NetF>*> input_vec;
clock_t start = clock();
_net->Forward(input_vec);//提特征
clock_t end = clock();
double totaltime;
totaltime = (double)(end - start) / CLOCKS_PER_SEC;
boost::shared_ptr<caffe::Blob<NetF>> fc5 = _net->blob_by_name("fc5");
const NetF* pstart = fc5->cpu_data();//*pstart只代表数组的第一个数
std::vector<double> V1;
for (int i = 0; i < 1024; i++)//循环获取特征数据,1张图片特征为512个
{
std::cout << *pstart << endl;
V1.push_back(*pstart);
pstart++;
}
cout << "\n此程序的运行时间为" << totaltime << "秒!" << endl;
cvWaitKey(0);
return 0;
}
配置文件数据层:
layer{
name: "data"
type: "MemoryData"
top: "data"
top: "label"
memory_data_param{
batch_size:2
channels:3
height:112
width:96
}
}
参考:http://blog.youkuaiyun.com/sunshine_in_moon/article/details/50126307
Demo:点击下载
代码更新:
//mat转Datum类型
bool CvMatToDatumSignalChannel(const cv::Mat& cv_mat, Datum* datum){
if (cv_mat.empty())
return false;
int channels = cv_mat.channels();
datum->set_channels(cv_mat.channels());
datum->set_height(cv_mat.rows);
datum->set_width(cv_mat.cols);
datum->set_label(0);
datum->clear_data();
datum->clear_float_data();
datum->set_encoded(false);
int datum_height = datum->height();
int datum_width = datum->width();
if(channels == 3){
for(int c = 0;c < channels;c++){
for (int h = 0; h < datum_height; ++h){
for (int w = 0; w < datum_width; ++w){
const float* ptr = cv_mat.ptr<float>(h);
datum->add_float_data(ptr[w*channels+c]);
}
}
}
}
return true;
}
//提特征
Mat getFeature(Mat image,vector<Point2f> points){
Mat warp_mat, cropImg, cropImg_;
cropImg = matlabtransform(image,points);//crop图片,可使用其他方法。
cropImg.convertTo(cropImg, CV_32FC3);
cropImg = (cropImg - 127.5) / 128;//均值归一化图片
flip(cropImg, cropImg_, 1);//镜像翻转
boost::shared_ptr<MemoryDataLayer<float> > mem_data_layer;
mem_data_layer = boost::static_pointer_cast<MemoryDataLayer<float> >(_net->layers()[0]);
Datum datum,datum_;
CvMatToDatumSignalChannel(cropImg,&datum);
CvMatToDatumSignalChannel(cropImg_,&datum_);
std::vector<Datum> datum_vector;
datum_vector.push_back(datum);
datum_vector.push_back(datum_);
mem_data_layer->set_batch_size(2);
mem_data_layer->AddDatumVector(datum_vector);
_net->Forward();
boost::shared_ptr<caffe::Blob<NetF> > fc5 = _net->blob_by_name("fc5");
const NetF* pstart = fc5->cpu_data();//*pstart只代表数组的第一个数
cv::Mat tmp(1024, 1, CV_32FC1);//获取特征值
for(int i=0;i<1024;i++){
for(int j=0;j<1;j++){
tmp.at<float>(i,j) = (float)*pstart;
pstart++;
}
}
return tmp;
}
mTransform.m
function [R,G,B] = mTransform(B_image,G_image,R_image,facial5points)
image(:,:,1) = R_image;
image(:,:,2) = G_image;
image(:,:,3) = B_image;
image = permute(image, [2,1,3]);
imgSize = [112, 96];
coord5points = [30.2946, 65.5318, 48.0252, 33.5493, 62.7299; ...
51.6963, 51.5014, 71.7366, 92.3655, 92.2041];
Tfm = cp2tform(facial5points', coord5points', 'similarity');
cropImg = imtransform(image, Tfm, 'XData', [1 imgSize(2)],...
'YData', [1 imgSize(1)], 'Size', imgSize);
R = cropImg(:,:,1);
G = cropImg(:,:,2);
B = cropImg(:,:,3);
end