非刚性人脸跟踪(四)
2015-05-10 11:00
465 查看
人脸检测与初始化
获得face_detector对象
可视化face_detector
人脸跟踪
训练与可视化
存在问题:如何使用视频第一帧来初始化模型。
解决方法:最简单的是使用opencv内置的级联检测器来搜索人脸,但是模型的检测区域位置将取决于对所跟踪的面部特征的选择。一种基于数据驱动模式的简单方案是,学习人脸检测区域与人脸特征的几何关系。
实现:定义一个face_detector类
detector_fname:保存用于级联分类的文件名;
detect_offset:偏移量集合,保存图像的检测区域到人脸位置和尺度的偏移量;
reference:参考形状;
detector:保存着人脸检测器;
detect函数是人脸跟踪的主要函数,该函数将一幅图像作为输入,该函数返回对图像中面部特征位置的粗略估计。具体实现:
(1) 彩色图像转化成灰度图像,并进行直方图均衡化
(2) 利用Opencv的级联分类器检测人脸位置
(3) 根据人脸外界矩形,结合detector_offset和参考形状模型,重新计算标注点的坐标
detect_ofdset:由三部分构成:
偏移量(x,y):表示人脸中心到人脸检测方框的距离;
尺度因子:表示为了最好的适应图像中的人脸,调整参考形状大小的比例。
这三部分都是检测方框宽度的线性函数。
问题:检测方框宽度与detect_ofdset变量之间的线性关系如何实现?
解决:加载训练数据到内存并分配参考形状后,使用train函数从标注数据中学习。
具体实现:
训练过程:每幅图首先利用opencv级联分离检索人脸位置,然后在样本图像上标出标注点。接下来判断矩形内标注点的数量是否合理,最后计算我们的偏移量detector_offset。
显示训练过程的标注结果:
具体实现
检测效果图:
这么做的好处:在用融合了几何关系的人脸特征模版匹配法跟踪人脸时,即便空间噪声满足高斯近乎于高斯分布,其检测效果也“最像“人脸的形状。
跟踪实现:
fps_timer类:计算程序运算的速度XX帧/秒,在face_tracker类中track函数调用。
face_tracker_params类:完成face_tracker中基本参数的初始化、序列化存储,包括搜索区域集合,最大迭代次数,级联分类器参数等等。
face_tracker类:人脸跟踪的核心模块,也是本次介绍的重点,包含train和track两个部分。
face_tracker::track函数,实现两种功能。当tracking标志位为fasle时,表示跟踪器进入检测模式(detectmode),为第一帧或下一帧图像初始化的人脸特征;当tracking标志位为true时,则根据上一帧人脸特征点的位置估计下一帧的人脸特征,这个操作主要由fit函数完成。跟踪算法的核心是多层次拟合(multi-level-fitting)过程。
face_tracker::fit函数的主要功能:给定一帧图像及上一帧人脸特征点集,在当前图像上搜索该点集附近的人脸特征,并产生新的人脸特征点集。
参数:
image:当前帧灰度图像
init:上一帧人脸特征点集(几何位置)
ssize:搜索区域大小
robust:标志位,决定是否采用robustmodel fitting流程,应对人脸特征的孤立点
itol:robust modelfitting迭代上限
ftol:迭代收敛判断阈值
返回值:
pts:在给定的搜索区域大小后,当前帧中人脸特征位置点集
结果:
代码:http://download.csdn.net/detail/yiluoyan/8684973
reference:/article/2586488.html
获得face_detector对象
可视化face_detector
人脸跟踪
训练与可视化
人脸检测与初始化
人脸跟踪方法:假设图像中所找到的面部特征与当前的估计比较接近。存在问题:如何使用视频第一帧来初始化模型。
解决方法:最简单的是使用opencv内置的级联检测器来搜索人脸,但是模型的检测区域位置将取决于对所跟踪的面部特征的选择。一种基于数据驱动模式的简单方案是,学习人脸检测区域与人脸特征的几何关系。
实现:定义一个face_detector类
class face_detector //face detector for initialsation { public: string detector_fname; //file containing cascade classifier Vec3f detector_offset; //offset from center of dectection Mat reference; //reference shape CascadeClassifier detector; //face detector vector<Point2f> //points for detected face in image dectect (const Mat &im, //image containing face const float scaleFactor = 1.1, //scale increment const int minNeighbours = 2, //minimum neighbourhood size const Size minSize = Size(30,30)); //minimum detection window size void train(ft_data &data, //trining data const string fname, //cascade detector const Mat &ref, //reference shape const bool visi = false, //visualise training? const bool mirror = false, //mirror datd? const float frac = 0.8, //fraction of points in detected rect const float scaleFactor = 1.1, //scale increment const int minNeight = 2, //minimum neightbourhood size const Size minSize = Size(30,30)); //minimum detection window size void write(FileStorage &fs) const; //file storage object to write to void read(const FileNode &node); //file stroge node to raed from protected: bool //are enough points bounded? enough_bounded_points(const Mat &pts, //points to evaluate const Rect R, //bounding retctangle const float frac); //fraction of points bounded Point2f //center of mass center_of_mass(const Mat &pts); //[x1;y1;...;xn;yn] float //scaling from reference to pts calc_scale(const Mat &pts); //[x1;y1;...;xn;yn] };
detector_fname:保存用于级联分类的文件名;
detect_offset:偏移量集合,保存图像的检测区域到人脸位置和尺度的偏移量;
reference:参考形状;
detector:保存着人脸检测器;
detect函数是人脸跟踪的主要函数,该函数将一幅图像作为输入,该函数返回对图像中面部特征位置的粗略估计。具体实现:
(1) 彩色图像转化成灰度图像,并进行直方图均衡化
(2) 利用Opencv的级联分类器检测人脸位置
(3) 根据人脸外界矩形,结合detector_offset和参考形状模型,重新计算标注点的坐标
vector<Point2f> face_detector::dectect(const Mat &im, const float scaleFactor /* = 1.1 */, const int minNeighbours /* = 2 */, const Size minSize /* = Size */) { //convert image to grayscale Mat gray; if(im.channels() == 1) gray = im; else cvtColor(im, gray, CV_RGB2GRAY); //face dectected vector<Rect> faces; Mat eqIm; equalizeHist(gray, eqIm); //跟踪图像中最明显的人脸 detector.detectMultiScale(eqIm, faces,scaleFactor,minNeighbours, CV_HAAR_FIND_BIGGEST_OBJECT, minSize); //根据人脸检测方框来将参考形状放到图像中 Rect R = faces[0]; //人脸外接矩形 Vec3f scale = detector_offset * R.width; int n = reference.rows / 2; vector<Point2f> p(n); for (int i = 0; i < n ; i++) { //scale[2]:缩放比例 p[i].x = scale[2]*reference.at<float>(2*i) + R.x + 0.5*R.width + scale[0]; p[i].y = scale[2]*reference.at<float>(2*i + 1) + R.y + 0.5*R.height + scale[1]; } return p; }
detect_ofdset:由三部分构成:
偏移量(x,y):表示人脸中心到人脸检测方框的距离;
尺度因子:表示为了最好的适应图像中的人脸,调整参考形状大小的比例。
这三部分都是检测方框宽度的线性函数。
问题:检测方框宽度与detect_ofdset变量之间的线性关系如何实现?
解决:加载训练数据到内存并分配参考形状后,使用train函数从标注数据中学习。
具体实现:
训练过程:每幅图首先利用opencv级联分离检索人脸位置,然后在样本图像上标出标注点。接下来判断矩形内标注点的数量是否合理,最后计算我们的偏移量detector_offset。
void face_detector::train(ft_data &data, const string fname, const Mat &ref, const bool visi /* = false */, const bool mirror /* = false */, const float frac /* = 0.8 */, const float scaleFactor /* = 1.1 */, const int minNeighbours /* = 2 */, const Size minSize /* = Size */) { char names[20] = {0}; //载入级联分配器 detector.load(fname.c_str()); detector_fname = fname; reference = ref.clone(); vector<float>xoffset(0), yoffset(0),zoffset(0); for (int i = 0; i < data.n_images(); i++) { //获取每一种训练图片 Mat im = data.get_image(i,0); if(im.empty()) continue; //获取训练图像对应的标注点 vector<Point2f> p = data.get_points(i,false); int n = p.size(); Mat pt = Mat(p).reshape(1, 2*n); vector<Rect> faces; Mat eqIm; equalizeHist(im, eqIm); //直方图均衡 //人脸检测 detector.detectMultiScale(eqIm, faces, scaleFactor, minNeighbours,CV_HAAR_FIND_BIGGEST_OBJECT, minSize); if(faces.size() >= 1) { if (visi) { sprintf(names,"train%d.jpg",i); //框出人脸区域 Mat I; cvtColor(im,I,CV_GRAY2BGR); for(int i = 0; i < n; i++) circle(I, p[i], 1, CV_RGB(0,255,0),2,CV_AA); rectangle(I,faces[0].tl(), faces[0].br(), CV_RGB(255,0,0), 3); imshow("face detector training", I); waitKey(10); imwrite(names,I); } //检查检测结果,确保是否有足够多的标注点 if (this->enough_bounded_points(pt, faces[0], frac)) { Point2f center = this->center_of_mass(pt); float w = faces[0].width; xoffset.push_back((center.x - (faces[0].x + 0.5 * faces[0].width)) / w); yoffset.push_back((center.y - (faces[0].y + 0.5 * faces[0].height))/w); zoffset.push_back(this->calc_scale(pt) / w); } } if (mirror) { im = data.get_image(i,1); if(im.empty()) continue; p = data.get_points(i,true); pt = Mat(p).reshape(1, 2*n); equalizeHist(im, eqIm); detector.detectMultiScale(eqIm, faces, scaleFactor, minNeighbours,CV_HAAR_FIND_BIGGEST_OBJECT,minSize); if (faces.size() >= 1) { if(visi) { Mat I; cvtColor(im, I,CV_GRAY2BGR); for (int i = 0; i < n; i++) circle(I, p[i], 1, CV_RGB(0,255,0), 2, CV_AA); rectangle(I, faces[0].tl(), faces[0].br(), CV_RGB(255,0,0),3); imshow("face detector training",I); waitKey(10); } //check ifenough points are in detected rectangle if (this->enough_bounded_points(pt, faces[0], frac)) { Point2f center = this->center_of_mass(pt); float w = faces[0].width; xoffset.push_back((center.x - (faces[0].x + 0.5 * faces[0].width)) / w); yoffset.push_back((center.y - (faces[0].y + 0.5 * faces[0].height))/w); zoffset.push_back(this->calc_scale(pt) / w); } } } } //chosse median value 选取集合中值 Mat X = Mat(xoffset), Xsort, Y = Mat(yoffset), Ysort, Z = Mat(zoffset), Zsort; cv::sort(X,Xsort,CV_SORT_EVERY_COLUMN|CV_SORT_ASCENDING); int nx = Xsort.rows; cv::sort(Y, Ysort, CV_SORT_EVERY_COLUMN|CV_SORT_ASCENDING); int ny = Ysort.rows; cv::sort(Z, Zsort, CV_SORT_EVERY_COLUMN|CV_SORT_ASCENDING); int nz = Zsort.rows; detector_offset = Vec3f(Xsort.at<float>(nx/2), Ysort.at<float>(ny/2), Zsort.at<float>(na/2)); return; }
显示训练过程的标注结果:
获得face_detector对象
与形状模型和块模型一样,我们需要获得face_detector对象并将其保存,用于后续的跟踪系统中。具体实现
#include "ft.h" #include "ft_data.h" #include "face_detector.h" #include "shape_model.h" #include <highgui.h> #include <iostream> int main() { float frac = 0.8; bool mirror = false; //load data ft_data data = load_ft<ft_data>("annotations.xml"); shape_model smodel = load_ft<shape_model>("shape.xml"); smodel.set_identity_params(); vector<Point2f> r = smodel.calc_shape(); Mat ref = Mat(r).reshape(1, 2*r.size()); //train face detector 训练和保存人脸检测器 face_detector detector; detector.train(data, "harrcascade_frontalface_alt.xml", ref, mirror, true, frac); //save detector save_ft<face_detector>("detector.xml", detector); return 0 ; }
可视化face_detector
跟之前的每个模块的训练与可视化一样,在我们得到face_detector类的detector.xml文件后, 我们可以利用该文件来检测人脸,实现如下:#include "ft.h" #include "face_detector.h" #include <highgui.h> #include <iostream> int main() { //load detector model face_detector detector = load_ft<face_detector>("detector.xml"); //detect untial user quits namedWindow("face detector"); Mat im = imread("test.jpg", 1); vector <Point2f> p = detector.dectect(im); if (p.size() > 0) { for (int i = 0; i < int(p.size()); i++) { circle(im, p[i], 1, CV_RGB(0,255,0), 2, CV_AA); } } imwrite("result.jpg", im); imshow("face detector", im); waitKey(0); destroyAllWindows; return 0; }
检测效果图:
人脸跟踪
实现:将人脸特征提取的结果投影到形状模型的线性子空间(shape model),也就是最小化原始点集到其在人脸子空间最接近合理形状分布的投影点集的距离。(就是说,把通过模版匹配检测到的原始点集A投影到人脸子空间产生新的点集B,再按照某种约束规则,通过对A迭代变化,使得A’到B的距离最小)。这么做的好处:在用融合了几何关系的人脸特征模版匹配法跟踪人脸时,即便空间噪声满足高斯近乎于高斯分布,其检测效果也“最像“人脸的形状。
跟踪实现:
#ifndef _FT_FACE_TRACKER_HPP_ #define _FT_FACE_TRACKER_HPP_ #include "patch_model.hpp" #include "shape_model.hpp" #include "face_detector.hpp" class fps_timer{ //frames/second timer for tracking public: int64 t_start; //start time int64 t_end; //end time float fps; //current frames/sec int fnum; //number of frames since @t_start fps_timer(){this->reset();} //default constructor void increment(); //increment timer index void reset(); //reset timer void display_fps(Mat &im, //image to display FPS on Point p = Point(-1,-1)); //bottom left corner of text }; class face_tracker_params{ //face tracking parameters public: vector<Size> ssize; //search region size/level bool robust; //use robust fitting? int itol; //maximum number of iterations to try float ftol; //convergence tolerance float scaleFactor; //OpenCV Cascade detector parameters int minNeighbours; //... Size minSize; //... face_tracker_params(); //sets default parameter settings void write(FileStorage &fs) const; //file storage object to write to void read(const FileNode& node); //file storage node to read from }; class face_tracker{ //face tracking class public: bool tracking; //are we in tracking mode? fps_timer timer; //frames/second timer vector<Point2f> points; //current tracked points face_detector detector; //detector for initialisation shape_model smodel; //shape model patch_models pmodel; //feature detectors face_tracker(){tracking = false;} int //0 = failure track(const Mat &im, //image containing face const face_tracker_params &p = //fitting parameters face_tracker_params()); //default tracking parameters void reset(){ //reset tracker tracking = false; timer.reset(); } void draw(Mat &im, const Scalar pts_color = CV_RGB(255,0,0), const Scalar con_color = CV_RGB(0,255,0)); void write(FileStorage &fs) const; //file storage object to write to void read(const FileNode& node); //file storage node to read from protected: vector<Point2f> //points for fitted face in image fit(const Mat &image, //image containing face const vector<Point2f> &init, //initial point estimates const Size ssize = Size(21,21), //search region size const bool robust = false, //use robust fitting? const int itol = 10, //maximum number of iterations to try const float ftol = 1e-3); //convergence tolerance }; #endif
fps_timer类:计算程序运算的速度XX帧/秒,在face_tracker类中track函数调用。
face_tracker_params类:完成face_tracker中基本参数的初始化、序列化存储,包括搜索区域集合,最大迭代次数,级联分类器参数等等。
face_tracker类:人脸跟踪的核心模块,也是本次介绍的重点,包含train和track两个部分。
face_tracker::track函数,实现两种功能。当tracking标志位为fasle时,表示跟踪器进入检测模式(detectmode),为第一帧或下一帧图像初始化的人脸特征;当tracking标志位为true时,则根据上一帧人脸特征点的位置估计下一帧的人脸特征,这个操作主要由fit函数完成。跟踪算法的核心是多层次拟合(multi-level-fitting)过程。
int face_tracker::track(const Mat &im,const face_tracker_params &p) { //convert image to greyscale Mat gray; if(im.channels()==1) gray = im; else cvtColor(im,gray,CV_RGB2GRAY); //initialise,为第一帧或下一帧初始化人脸特征 if(!tracking) points = detector.detect(gray,p.scaleFactor,p.minNeighbours,p.minSize); if((int)points.size() != smodel.npts()) return 0; //fit,通过迭代的方式,估计当前帧中的人脸特征点 for(int level = 0; level < int(p.ssize.size()); level++) points = this->fit(gray,points,p.ssize[level],p.robust,p.itol,p.ftol); //set tracking flag and increment timer tracking = true; timer.increment(); return 1; } .... vector<Point2f>face_tracker::fit(const Mat &image, const vector<Point2f> &init, const Size ssize,const bool robust,const int itol,const float ftol) { int n = smodel.npts(); assert((int(init.size())==n) && (pmodel.n_patches()==n)); smodel.calc_params(init); vector<Point2f> pts = smodel.calc_shape(); //find facial features in image around current estimates vector<Point2f> peaks = pmodel.calc_peaks(image,pts,ssize); //optimise if(!robust){ smodel.calc_params(peaks); //compute shape model parameters pts = smodel.calc_shape(); //update shape }else{ Mat weight(n,1,CV_32F),weight_sort(n,1,CV_32F); vector<Point2f> pts_old = pts; for(int iter = 0; iter < itol; iter++){ //compute robust weight for(int i = 0; i < n; i++)weight.fl(i) = norm(pts[i] - peaks[i]); cv::sort(weight,weight_sort,CV_SORT_EVERY_COLUMN|CV_SORT_ASCENDING); double var = 1.4826*weight_sort.fl(n/2); if(var < 0.1)var = 0.1; pow(weight,2,weight); weight *= -0.5/(var*var); cv::exp(weight,weight); //compute shape model parameters smodel.calc_params(peaks,weight); //update shape pts = smodel.calc_shape(); //check for convergence float v = 0; for(int i = 0; i < n; i++)v += norm(pts[i]-pts_old[i]); if(v < ftol)break; else pts_old = pts; } }return pts; }
face_tracker::fit函数的主要功能:给定一帧图像及上一帧人脸特征点集,在当前图像上搜索该点集附近的人脸特征,并产生新的人脸特征点集。
参数:
image:当前帧灰度图像
init:上一帧人脸特征点集(几何位置)
ssize:搜索区域大小
robust:标志位,决定是否采用robustmodel fitting流程,应对人脸特征的孤立点
itol:robust modelfitting迭代上限
ftol:迭代收敛判断阈值
返回值:
pts:在给定的搜索区域大小后,当前帧中人脸特征位置点集
训练与可视化
跟踪实现:#include "ft.hpp" #include "face_tracker.hpp" #include <opencv2/highgui/highgui.hpp> #include <iostream> #define fl at<float> void draw_string(Mat img, //image to draw on const string text) //text to draw { Size size = getTextSize(text,FONT_HERSHEY_COMPLEX,0.6f,1,NULL); putText(img,text,Point(0,size.height),FONT_HERSHEY_COMPLEX,0.6f, Scalar::all(0),1,CV_AA); putText(img,text,Point(1,size.height+1),FONT_HERSHEY_COMPLEX,0.6f, Scalar::all(255),1,CV_AA); } int main(int argc,char** argv) { //load detector model Mat im; face_tracker tracker = load_ft<face_tracker>("tracker.xml"); //create tracker parameters face_tracker_params p; p.robust = false; p.ssize.resize(3); p.ssize[0] = Size(21,21); p.ssize[1] = Size(11,11); p.ssize[2] = Size(5,5); //open video stream VideoCapture cam; namedWindow("face tracker"); cam.open("test.avi"); if(!cam.isOpened()){ cout << "Failed opening video file." << endl; return 0; } //detect until user quits while(cam.get(CV_CAP_PROP_POS_AVI_RATIO) < 0.999999){ cam >> im; if(tracker.track(im,p)) tracker.draw(im); draw_string(im,"d - redetection"); tracker.timer.display_fps(im,Point(1,im.rows-1)); imshow("face tracker",im); int c = waitKey(10); if(c == 'q') break; else if(c == 'd') tracker.reset();![这里写图片描述](http://img.blog.csdn.net/20150510202317519) } destroyWindow("face tracker"); cam.release(); return 0; }
结果:
代码:http://download.csdn.net/detail/yiluoyan/8684973
reference:/article/2586488.html
相关文章推荐
- 非刚性人脸跟踪 —— 人脸检测与初始化
- 非刚性人脸跟踪(一)
- 《Master Opencv...读书笔记》非刚性人脸跟踪 II
- 非刚性人脸跟踪(二)
- (学习笔记)第六章 非刚性人脸跟踪——annotation(标记)
- (学习笔记)第六章 非刚性人脸跟踪——Face tracking(人脸跟踪)
- 非刚性人脸跟踪 —— 面部特征检测器
- 《Master Opencv...读书笔记》非刚性人脸跟踪 IV (终)
- 非刚性人脸跟踪 —— 几何约束
- (学习笔记)第六章 非刚性人脸跟踪——annotation(标记)
- 刚性人脸跟踪 开始
- 《Master Opencv...读书笔记》非刚性人脸跟踪 I
- 《Master Opencv...读书笔记》非刚性人脸跟踪 II
- 非刚性人脸跟踪(三)
- 《Master Opencv...读书笔记》非刚性人脸跟踪 III
- 学习非刚性人脸跟踪
- 《Master Opencv...读书笔记》非刚性人脸跟踪 III
- 非刚性人脸跟踪 —— 人脸跟踪
- 《Master Opencv...读书笔记》非刚性人脸跟踪 IV (终)
- 非刚性人脸跟踪 —— 系统概述