从一个人脸数据集中获取人脸的五官位置
// caffe
#include <caffe/caffe.hpp>
#include <caffe/layers/memory_data_layer.hpp>
// c++
#include <string>
#include <vector>
#include <fstream>
// opencv
#include <opencv2/opencv.hpp>
// boost
#include "boost/make_shared.hpp"
#include<iostream>
#define CPU_ONLY
using namespace caffe;
using namespace std;
typedef struct FaceRect {
float x1;
float y1;
float x2;
float y2;
float score; /**< Larger score should mean higher confidence. */
} FaceRect;
typedef struct FacePts {
float x[5],y[5];
} FacePts;
typedef struct FaceInfo {
FaceRect bbox;
cv::Vec4f regression;
FacePts facePts;
double roll;
double pitch;
double yaw;
} FaceInfo;
class MTCNN {
public:
MTCNN(const string& proto_model_dir);
void Detect(const cv::Mat& img, std::vector<FaceInfo> &faceInfo, int minSize, double* threshold, double factor);
private:
bool CvMatToDatumSignalChannel(const cv::Mat& cv_mat, Datum* datum);
void Preprocess(const cv::Mat& img,
std::vector<cv::Mat>* input_channels);
void WrapInputLayer(std::vector<cv::Mat>* input_channels,Blob<float>* input_layer,
const int height,const int width);
void SetMean();
void GenerateBoundingBox( Blob<float>* confidence,Blob<float>* reg,
float scale,float thresh,int image_width,int image_height);
void ClassifyFace(const std::vector<FaceInfo>& regressed_rects,cv::Mat &sample_single,
boost::shared_ptr<Net<float> >& net,double thresh,char netName);
void ClassifyFace_MulImage(const std::vector<FaceInfo> ®ressed_rects, cv::Mat &sample_single,
boost::shared_ptr<Net<float> >& net, double thresh, char netName);
std::vector<FaceInfo> NonMaximumSuppression(std::vector<FaceInfo>& bboxes,float thresh,char methodType);
void Bbox2Square(std::vector<FaceInfo>& bboxes);
void Padding(int img_w, int img_h);
std::vector<FaceInfo> BoxRegress(std::vector<FaceInfo> &faceInfo_, int stage);
void RegressPoint(const std::vector<FaceInfo>& faceInfo);
private:
boost::shared_ptr<Net<float> > PNet_;
boost::shared_ptr<Net<float> > RNet_;
boost::shared_ptr<Net<float> > ONet_;
// x1,y1,x2,t2 and score
std::vector<FaceInfo> condidate_rects_;
std::vector<FaceInfo> total_boxes_;
std::vector<FaceInfo> regressed_rects_;
std::vector<FaceInfo> regressed_pading_;
std::vector<cv::Mat> crop_img_;
int curr_feature_map_w_;
int curr_feature_map_h_;
int num_channels_;
};
// compare score
bool CompareBBox(const FaceInfo & a, const FaceInfo & b) {
return a.bbox.score > b.bbox.score;
}
// methodType : u is IoU(Intersection Over Union)
// methodType : m is IoM(Intersection Over Maximum)
std::vector<FaceInfo> MTCNN::NonMaximumSuppression(std::vector<FaceInfo>& bboxes,
float thresh,char methodType){
std::vector<FaceInfo> bboxes_nms;
std::sort(bboxes.begin(), bboxes.end(), CompareBBox);
int32_t select_idx = 0;
int32_t num_bbox = static_cast<int32_t>(bboxes.size());
std::vector<int32_t> mask_merged(num_bbox, 0);
bool all_merged = false;
while (!all_merged) {
while (select_idx < num_bbox && mask_merged[select_idx] == 1)
select_idx++;
if (select_idx == num_bbox) {
all_merged = true;
continue;
}
bboxes_nms.push_back(bboxes[select_idx]);
mask_merged[select_idx] = 1;
FaceRect select_bbox = bboxes[select_idx].bbox;
float area1 = static_cast<float>((select_bbox.x2-select_bbox.x1+1) * (select_bbox.y2-select_bbox.y1+1));
float x1 = static_cast<float>(select_bbox.x1);
float y1 = static_cast<float>(select_bbox.y1);
float x2 = static_cast<float>(select_bbox.x2);
float y2 = static_cast<float>(select_bbox.y2);
select_idx++;
for (int32_t i = select_idx; i < num_bbox; i++) {
if (mask_merged[i] == 1)
continue;
FaceRect& bbox_i = bboxes[i].bbox;
float x = std::max<float>(x1, static_cast<float>(bbox_i.x1));
float y = std::max<float>(y1, static_cast<float>(bbox_i.y1));
float w = std::min<float>(x2, static_cast<float>(bbox_i.x2)) - x + 1;
float h = std::min<float>(y2, static_cast<float>(bbox_i.y2)) - y + 1;
if (w <= 0 || h <= 0)
continue;
float area2 = static_cast<float>((bbox_i.x2-bbox_i.x1+1) * (bbox_i.y2-bbox_i.y1+1));
float area_intersect = w * h;
switch (methodType) {
case 'u':
if (static_cast<float>(area_intersect) / (area1 + area2 - area_intersect) > thresh)
mask_merged[i] = 1;
break;
case 'm':
if (static_cast<float>(area_intersect) / std::min(area1 , area2) > thresh)
mask_merged[i] = 1;
break;
default:
break;
}
}
}
return bboxes_nms;
}
void MTCNN::Bbox2Square(std::vector<FaceInfo>& bboxes){
for(int i=0;i<bboxes.size();i++){
float h = bboxes[i].bbox.x2 - bboxes[i].bbox.x1;
float w = bboxes[i].bbox.y2 - bboxes[i].bbox.y1;
float side = h>w ? h:w;
bboxes[i].bbox.x1 += (h-side)*0.5;
bboxes[i].bbox.y1 += (w-side)*0.5;
bboxes[i].bbox.x2 = (int)(bboxes[i].bbox.x1 + side);
bboxes[i].bbox.y2 = (int)(bboxes[i].bbox.y1 + side);
bboxes[i].bbox.x1 = (int)(bboxes[i].bbox.x1);
bboxes[i].bbox.y1 = (int)(bboxes[i].bbox.y1);
}
}
std::vector<FaceInfo> MTCNN::BoxRegress(std::vector<FaceInfo>& faceInfo,int stage){
std::vector<FaceInfo> bboxes;
for(int bboxId =0;bboxId<faceInfo.size();bboxId++){
FaceRect faceRect;
FaceInfo tempFaceInfo;
float regw = faceInfo[bboxId].bbox.y2 - faceInfo[bboxId].bbox.y1;
regw += (stage == 1)? 0:1;
float regh = faceInfo[bboxId].bbox.x2 - faceInfo[bboxId].bbox.x1;
regh += (stage == 1)? 0:1;
faceRect.y1 = faceInfo[bboxId].bbox.y1 + regw * faceInfo[bboxId].regression[0];
faceRect.x1 = faceInfo[bboxId].bbox.x1 + regh * faceInfo[bboxId].regression[1];
faceRect.y2 = faceInfo[bboxId].bbox.y2 + regw * faceInfo[bboxId].regression[2];
faceRect.x2 = faceInfo[bboxId].bbox.x2 + regh * faceInfo[bboxId].regression[3];
faceRect.score = faceInfo[bboxId].bbox.score;
tempFaceInfo.bbox = faceRect;
tempFaceInfo.regression = faceInfo[bboxId].regression;
if(stage == 3)
tempFaceInfo.facePts = faceInfo[bboxId].facePts;
bboxes.push_back(tempFaceInfo);
}
return bboxes;
}
// compute the padding coordinates (pad the bounding boxes to square)
void MTCNN::Padding(int img_w,int img_h){
for(int i=0;i<regressed_rects_.size();i++){
FaceInfo tempFaceInfo;
tempFaceInfo = regressed_rects_[i];
tempFaceInfo.bbox.y2 = (regressed_rects_[i].bbox.y2 >= img_w) ? img_w : regressed_rects_[i].bbox.y2;
tempFaceInfo.bbox.x2 = (regressed_rects_[i].bbox.x2 >= img_h) ? img_h : regressed_rects_[i].bbox.x2;
tempFaceInfo.bbox.y1 = (regressed_rects_[i].bbox.y1 <1) ? 1 : regressed_rects_[i].bbox.y1;
tempFaceInfo.bbox.x1 = (regressed_rects_[i].bbox.x1 <1) ? 1 : regressed_rects_[i].bbox.x1;
regressed_pading_.push_back(tempFaceInfo);
}
}
void MTCNN::GenerateBoundingBox(Blob<float>* confidence,Blob<float>* reg,
float scale,float thresh,int image_width,int image_height){
int stride = 2;
int cellSize = 12;
int curr_feature_map_w_ = std::ceil((image_width - cellSize)*1.0/stride)+1;
int curr_feature_map_h_ = std::ceil((image_height - cellSize)*1.0/stride)+1;
//std::cout << "Feature_map_size:"<< curr_feature_map_w_ <<" "<<curr_feature_map_h_<<std::endl;
int regOffset = curr_feature_map_w_*curr_feature_map_h_;
// the first count numbers are confidence of face
int count = confidence->count()/2;
const float* confidence_data = confidence->cpu_data();
confidence_data += count;
const float* reg_data = reg->cpu_data();
condidate_rects_.clear();
for(int i=0;i<count;i++){
if(*(confidence_data+i)>=thresh){
int y = i / curr_feature_map_w_;
int x = i - curr_feature_map_w_ * y;
float xTop = (int)((x*stride+1)/scale);
float yTop = (int)((y*stride+1)/scale);
float xBot = (int)((x*stride+cellSize-1+1)/scale);
float yBot = (int)((y*stride+cellSize-1+1)/scale);
FaceRect faceRect;
faceRect.x1 = xTop;
faceRect.y1 = yTop;
faceRect.x2 = xBot;
faceRect.y2 = yBot;
faceRect.score = *(confidence_data+i);
FaceInfo faceInfo;
faceInfo.bbox = faceRect;
faceInfo.regression = cv::Vec4f(reg_data[i+0*regOffset],reg_data[i+1*regOffset],reg_data[i+2*regOffset],reg_data[i+3*regOffset]);
condidate_rects_.push_back(faceInfo);
}
}
}
MTCNN::MTCNN(const std::string &proto_model_dir){
#ifdef CPU_ONLY
Caffe::set_mode(Caffe::CPU);
#else
Caffe::set_mode(Caffe::GPU);
#endif
/* Load the network. */
PNet_.reset(new Net<float>((proto_model_dir+"/det1.prototxt"), TEST));
PNet_->CopyTrainedLayersFrom(proto_model_dir+"/det1.caffemodel");
CHECK_EQ(PNet_->num_inputs(), 1) << "Network should have exactly one input.";
CHECK_EQ(PNet_->num_outputs(),2) << "Network should have exactly two output, one"
" is bbox and another is confidence.";
#ifdef CPU_ONLY
RNet_.reset(new Net<float>((proto_model_dir+"/det2.prototxt"), TEST));
#else
RNet_.reset(new Net<float>((proto_model_dir+"/det2_input.prototxt"), TEST));
#endif
RNet_->CopyTrainedLayersFrom(proto_model_dir+"/det2.caffemodel");
// CHECK_EQ(RNet_->num_inputs(), 0) << "Network should have exactly one input.";
// CHECK_EQ(RNet_->num_outputs(),3) << "Network should have exactly two output, one"
// " is bbox and another is confidence.";
#ifdef CPU_ONLY
ONet_.reset(new Net<float>((proto_model_dir+"/det3.prototxt"), TEST));
#else
ONet_.reset(new Net<float>((proto_model_dir+"/det3_input.prototxt"), TEST));
#endif
ONet_->CopyTrainedLayersFrom(proto_model_dir+"/det3.caffemodel");
// CHECK_EQ(ONet_->num_inputs(), 1) << "Network should have exactly one input.";
// CHECK_EQ(ONet_->num_outputs(),3) << "Network should have exactly three output, one"
// " is bbox and another is confidence.";
Blob<float>* input_layer;
input_layer = PNet_->input_blobs()[0];
num_channels_ = input_layer->channels();
CHECK(num_channels_ == 3 || num_channels_ == 1) << "Input layer should have 1 or 3 channels.";
}
void MTCNN::WrapInputLayer(std::vector<cv::Mat>* input_channels,
Blob<float>* input_layer, const int height, const int width) {
float* input_data = input_layer->mutable_cpu_data();
for (int i = 0; i < input_layer->channels(); ++i) {
cv::Mat channel(height, width, CV_32FC1, input_data);
input_channels->push_back(channel);
input_data += width * height;
}
}
void MTCNN::ClassifyFace(const std::vector<FaceInfo>& regressed_rects,cv::Mat &sample_single,
boost::shared_ptr<Net<float> >& net,double thresh,char netName){
int numBox = regressed_rects.size();
Blob<float>* crop_input_layer = net->input_blobs()[0];
int input_channels = crop_input_layer->channels();
int input_width = crop_input_layer->width();
int input_height = crop_input_layer->height();
crop_input_layer->Reshape(1, input_channels, input_width, input_height);
net->Reshape();
condidate_rects_.clear();
// load crop_img data to datum
for(int i=0;i<numBox;i++){
std::vector<cv::Mat> channels;
WrapInputLayer(&channels,net->input_blobs()[0],input_width,input_height);
int pad_top = std::abs(regressed_pading_[i].bbox.x1 - regressed_rects[i].bbox.x1);
int pad_left = std::abs(regressed_pading_[i].bbox.y1 - regressed_rects[i].bbox.y1);
int pad_right = std::abs(regressed_pading_[i].bbox.y2 - regressed_rects[i].bbox.y2);
int pad_bottom= std::abs(regressed_pading_[i].bbox.x2 - regressed_rects[i].bbox.x2);
cv::Mat crop_img = sample_single(cv::Range(regressed_pading_[i].bbox.y1-1,regressed_pading_[i].bbox.y2),
cv::Range(regressed_pading_[i].bbox.x1-1,regressed_pading_[i].bbox.x2));
cv::copyMakeBorder(crop_img,crop_img,pad_left,pad_right,pad_top,pad_bottom,cv::BORDER_CONSTANT,cv::Scalar(0));
cv::resize(crop_img,crop_img,cv::Size(input_width,input_height),0,0,cv::INTER_AREA);
crop_img = (crop_img-127.5)*0.0078125;
cv::split(crop_img,channels);
CHECK(reinterpret_cast<float*>(channels.at(0).data) == net->input_blobs()[0]->cpu_data())
<< "Input channels are not wrapping the input layer of the network.";
net->Forward();
int reg_id = 0;
int confidence_id = 1;
if(netName == 'o') confidence_id = 2;
const Blob<float>* reg = net->output_blobs()[reg_id];
const Blob<float>* confidence = net->output_blobs()[confidence_id];
// ONet points_offset != NULL
const Blob<float>* points_offset = net->output_blobs()[1];
const float* confidence_data = confidence->cpu_data() + confidence->count()/2;
const float* reg_data = reg->cpu_data();
const float* points_data;
if(netName == 'o') points_data = points_offset->cpu_data();
if(*(confidence_data) > thresh){
FaceRect faceRect;
faceRect.x1 = regressed_rects[i].bbox.x1;
faceRect.y1 = regressed_rects[i].bbox.y1;
faceRect.x2 = regressed_rects[i].bbox.x2;
faceRect.y2 = regressed_rects[i].bbox.y2 ;
faceRect.score = *(confidence_data);
FaceInfo faceInfo;
faceInfo.bbox = faceRect;
faceInfo.regression = cv::Vec4f(reg_data[0],reg_data[1],reg_data[2],reg_data[3]);
// x x x x x y y y y y
if(netName == 'o'){
FacePts face_pts;
float w = faceRect.y2 - faceRect.y1 + 1;
float h = faceRect.x2 - faceRect.x1 + 1;
for(int j=0;j<5;j++){
face_pts.y[j] = faceRect.y1 + *(points_data+j) * h - 1;
face_pts.x[j] = faceRect.x1 + *(points_data+j+5) * w -1;
}
faceInfo.facePts = face_pts;
}
condidate_rects_.push_back(faceInfo);
}
}
regressed_pading_.clear();
}
// multi test image pass a forward
void MTCNN::ClassifyFace_MulImage(const std::vector<FaceInfo>& regressed_rects,cv::Mat &sample_single,
boost::shared_ptr<Net<float> >& net,double thresh,char netName){
condidate_rects_.clear();
int numBox = regressed_rects.size();
std::vector<Datum> datum_vector;
boost::shared_ptr<MemoryDataLayer<float> > mem_data_layer;
mem_data_layer = boost::static_pointer_cast<MemoryDataLayer<float> >(net->layers()[0]);
int input_width = mem_data_layer->width();
int input_height = mem_data_layer->height();
// load crop_img data to datum
for(int i=0;i<numBox;i++){
int pad_top = std::abs(regressed_pading_[i].bbox.x1 - regressed_rects[i].bbox.x1);
int pad_left = std::abs(regressed_pading_[i].bbox.y1 - regressed_rects[i].bbox.y1);
int pad_right = std::abs(regressed_pading_[i].bbox.y2 - regressed_rects[i].bbox.y2);
int pad_bottom= std::abs(regressed_pading_[i].bbox.x2 - regressed_rects[i].bbox.x2);
cv::Mat crop_img = sample_single(cv::Range(regressed_pading_[i].bbox.y1-1,regressed_pading_[i].bbox.y2),
cv::Range(regressed_pading_[i].bbox.x1-1,regressed_pading_[i].bbox.x2));
cv::copyMakeBorder(crop_img,crop_img,pad_left,pad_right,pad_top,pad_bottom,cv::BORDER_CONSTANT,cv::Scalar(0));
cv::resize(crop_img,crop_img,cv::Size(input_width,input_height),0,0,cv::INTER_AREA);
crop_img = (crop_img-127.5)*0.0078125;
Datum datum;
CvMatToDatumSignalChannel(crop_img,&datum);
datum_vector.push_back(datum);
}
regressed_pading_.clear();
/* extract the features and store */
mem_data_layer->set_batch_size(numBox);
mem_data_layer->AddDatumVector(datum_vector);
/* fire the network */
float no_use_loss = 0;
net->Forward(&no_use_loss);
// CHECK(reinterpret_cast<float*>(crop_img_set.at(0).data) == net->input_blobs()[0]->cpu_data())
// << "Input channels are not wrapping the input layer of the network.";
// return RNet/ONet result
std::string outPutLayerName = (netName == 'r' ? "conv5-2" : "conv6-2");
std::string pointsLayerName = "conv6-3";
const boost::shared_ptr<Blob<float> > reg = net->blob_by_name(outPutLayerName);
const boost::shared_ptr<Blob<float> > confidence = net->blob_by_name("prob1");
// ONet points_offset != NULL
const boost::shared_ptr<Blob<float> > points_offset = net->blob_by_name(pointsLayerName);
const float* confidence_data = confidence->cpu_data();
const float* reg_data = reg->cpu_data();
const float* points_data;
if(netName == 'o') points_data = points_offset->cpu_data();
for(int i=0;i<numBox;i++){
if(*(confidence_data+i*2+1) > thresh){
FaceRect faceRect;
faceRect.x1 = regressed_rects[i].bbox.x1;
faceRect.y1 = regressed_rects[i].bbox.y1;
faceRect.x2 = regressed_rects[i].bbox.x2;
faceRect.y2 = regressed_rects[i].bbox.y2 ;
faceRect.score = *(confidence_data+i*2+1);
FaceInfo faceInfo;
faceInfo.bbox = faceRect;
faceInfo.regression = cv::Vec4f(reg_data[4*i+0],reg_data[4*i+1],reg_data[4*i+2],reg_data[4*i+3]);
// x x x x x y y y y y
if(netName == 'o'){
FacePts face_pts;
float w = faceRect.y2 - faceRect.y1 + 1;
float h = faceRect.x2 - faceRect.x1 + 1;
for(int j=0;j<5;j++){
face_pts.y[j] = faceRect.y1 + *(points_data+j+10*i) * h - 1;
face_pts.x[j] = faceRect.x1 + *(points_data+j+5+10*i) * w -1;
}
faceInfo.facePts = face_pts;
}
condidate_rects_.push_back(faceInfo);
}
}
}
bool MTCNN::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;
}
void MTCNN::Detect(const cv::Mat& image,std::vector<FaceInfo>& faceInfo,int minSize,double* threshold,double factor){
// 2~3ms
// invert to RGB color space and float type
cv::Mat sample_single,resized;
image.convertTo(sample_single,CV_32FC3);
cv::cvtColor(sample_single,sample_single,cv::COLOR_BGR2RGB);
sample_single = sample_single.t();
int height = image.rows;
int width = image.cols;
int minWH = std::min(height,width);
int factor_count = 0;
double m = 12./minSize;
minWH *= m;
std::vector<double> scales;
while (minWH >=24)
{
scales.push_back(m * std::pow(factor,factor_count));
minWH *= factor;
++factor_count;
}
// 11ms main consum
Blob<float>* input_layer = PNet_->input_blobs()[0];
for(int i=0;i<factor_count;i++)
{
double scale = scales[i];
int ws = std::ceil(height*scale);
int hs = std::ceil(width*scale);
// wrap image and normalization using INTER_AREA method
cv::resize(sample_single,resized,cv::Size(ws,hs),0,0,cv::INTER_AREA);
resized.convertTo(resized, CV_32FC3, 0.0078125,-127.5*0.0078125);
// input data
input_layer->Reshape(1, 3, hs, ws);
PNet_->Reshape();
std::vector<cv::Mat> input_channels;
WrapInputLayer(&input_channels,PNet_->input_blobs()[0],hs,ws);
cv::split(resized,input_channels);
// check data transform right
CHECK(reinterpret_cast<float*>(input_channels.at(0).data) == PNet_->input_blobs()[0]->cpu_data())
<< "Input channels are not wrapping the input layer of the network.";
PNet_->Forward();
// return result
Blob<float>* reg = PNet_->output_blobs()[0];
//const float* reg_data = reg->cpu_data();
Blob<float>* confidence = PNet_->output_blobs()[1];
GenerateBoundingBox(confidence, reg, scale, threshold[0],ws,hs);
std::vector<FaceInfo> bboxes_nms = NonMaximumSuppression(condidate_rects_,0.5,'u');
total_boxes_.insert(total_boxes_.end(),bboxes_nms.begin(),bboxes_nms.end());
}
int numBox = total_boxes_.size();
if(numBox != 0){
total_boxes_ = NonMaximumSuppression(total_boxes_,0.7,'u');
regressed_rects_ = BoxRegress(total_boxes_,1);
total_boxes_.clear();
Bbox2Square(regressed_rects_);
Padding(width,height);
/// Second stage
#ifdef CPU_ONLY
ClassifyFace(regressed_rects_,sample_single,RNet_,threshold[1],'r');
#else
ClassifyFace_MulImage(regressed_rects_,sample_single,RNet_,threshold[1],'r');
#endif
condidate_rects_ = NonMaximumSuppression(condidate_rects_,0.7,'u');
regressed_rects_ = BoxRegress(condidate_rects_,2);
Bbox2Square(regressed_rects_);
Padding(width,height);
/// three stage
numBox = regressed_rects_.size();
if(numBox != 0){
#ifdef CPU_ONLY
ClassifyFace(regressed_rects_,sample_single,ONet_,threshold[2],'o');
#else
ClassifyFace_MulImage(regressed_rects_,sample_single,ONet_,threshold[2],'o');
#endif
regressed_rects_ = BoxRegress(condidate_rects_,3);
faceInfo = NonMaximumSuppression(regressed_rects_,0.7,'m');
}
}
regressed_pading_.clear();
regressed_rects_.clear();
condidate_rects_.clear();
}
cv::Mat getwarpAffineImg(cv::Mat &src,cv::Point2f leftEye,cv::Point2f rightEye)
{
//计算两眼中心点,按照此中心点进行旋转, 第31个为左眼坐标,36为右眼坐标
cv:: Point2f eyesCenter = cv::Point2f( (leftEye.x + rightEye.x) * 0.5f, (leftEye.y + rightEye.y) * 0.5f );
// 计算两个眼睛间的角度
double dy = (rightEye.y - leftEye.y);
double dx = (rightEye.x - leftEye.x);
double angle = atan2(dy, dx) * 180.0/CV_PI; // Convert from radians to degrees.
//由eyesCenter, andle, scale按照公式计算仿射变换矩阵,此时1.0表示不进行缩放
cv::Mat rot_mat = getRotationMatrix2D(eyesCenter, angle, 1.0);
// 进行仿射变换,变换后大小为src的大小
cv::Mat rot;
warpAffine(src, rot, rot_mat, src.size());
//imwrite("rot.jpg",rot);
return rot;
}
int main(int argc,char **argv)
{
double threshold[3] = {0.6,0.7,0.7};
double factor = 0.709;
int minSize = 40;
std::string proto_model_dir = "caffe/examples/MTSrc/MTmodel";//模型存放位置包括三个.prototxt和三个.model
std::string pic_root_dir = "pic/";//人脸数据集的位置
std::string ffp_root_dir = "./"
MTCNN detector(proto_model_dir);
//读取列表对每个图像进行处理
ifstream fin("dataset/lfw_all.list");//人脸数据集的列表
string img_path;
ofstream outfile;//保存图像及其信息的文件
outfile.open("result.txt");
while(getline(fin,img_path))
{
cv::Mat image0 = cv::imread(pic_root_dir + img_path);
cout<<img_path<<endl;
cv::Mat image;
cv::resize(image0,image,cv::Size(image0.cols,image0.rows));
std::vector<FaceInfo> faceInfo;
std::cout <<"Detect "<<image.rows<<"X"<<image.cols;
detector.Detect(image,faceInfo,minSize,threshold,factor);
//只画一张人脸
int face_num = 0;
if(faceInfo.size()>=1){
face_num = 1;
}else{
face_num = -1;
}
//描述五官的位置
cv::Mat warped_img;
for(int i=0;i<face_num;i++){
FacePts facePts = faceInfo[i].facePts;
//保存人脸的坐标信息
outfile<<img_path<<" ";
for(int j=0;j<5;j++){
outfile << facePts.y[j]<<" "<<facePts.x[j]<<" ";
}
outfile<<endl;
}
}
outfile.close();
printf("execute succeed\n");
return 1;
}