之前用dlib库检测人脸的68个特征点,虽然特征点比较准确,但如果被检测图片比较大,效率就比较低。而且脸部模型数据文件shape_predictor_68_face_landmarks.dat有95MB太大了。
后来发现flandmark这样一个轻量级的人脸检测库,大概只有5MB左右。检测的特征点少一些,只有8个。不过如果作一般用途,这8个点足够了。下面简单贴出相关代码。
主要参考来自于:http://cmp.felk.cvut.cz/~uricamic/flandmark/
下载的包里有例子,我作了一些修改,libflandmark也在里面。
声明所需的变量
#include "libflandmark/flandmark_detector.h"
/*
* 5 1 2 6
*
*
* 0/7
*
*
* 3 4
*
*/
CvHaarClassifierCascade* faceCascade;
FLANDMARK_Model * model;
初始化:
HSFaceFlandmark::HSFaceFlandmark()
{
char szModuleFilePath[MAX_PATH];
int n = GetModuleFileNameA(0, szModuleFilePath, MAX_PATH); //获得当前执行文件的路径
szModuleFilePath[ strrchr(szModuleFilePath, '\\') - szModuleFilePath + 1 ] = 0;//将最后一个"\\"后的字符置为0
DBG("================= '%s'\n", szModuleFilePath);
char faceCascadeFilename[MAX_PATH];
char flandmarkmodelFilename[MAX_PATH];
sprintf(faceCascadeFilename, "%s/haarcascade_frontalface_alt.xml",szModuleFilePath);
sprintf(flandmarkmodelFilename, "%s/flandmark_model.dat",szModuleFilePath);
// Haar Cascade file, used for Face Detection.
//char faceCascadeFilename[] = "./haarcascade_frontalface_alt.xml";
// Load the HaarCascade classifier for face detection.
faceCascade = (CvHaarClassifierCascade*)cvLoad(faceCascadeFilename, 0, 0, 0);
if( !faceCascade )
{
DBG("Couldnt load Face detector '%s'\n", faceCascadeFilename);
}
model = flandmark_init(flandmarkmodelFilename);
if (model == 0)
{
DBG("Structure model wasn't created. Corrupted file flandmark_model.dat?\n");
}
}
8个关键点检测的API
int detectFaceInImage(IplImage *orig, cv::Mat& img, double *landmarks)
{
IplImage frame = IplImage(img);
IplImage *input = cvCreateImage(cvSize(frame.width, frame.height), IPL_DEPTH_8U, 1);
cvConvertImage(&frame, input);
int bbox[4] = {0};
// Smallest face size.
CvSize minFeatureSize = cvSize(80, 80);
int flags = CV_HAAR_DO_CANNY_PRUNING;
// How detailed should the search be.
float search_scale_factor = 1.1f;
CvMemStorage* storage;
CvSeq* rects;
int nFaces;
storage = cvCreateMemStorage(0);
cvClearMemStorage(storage);
// Detect all the faces in the greyscale image.
rects = cvHaarDetectObjects(input, faceCascade, storage, search_scale_factor, 2, flags, minFeatureSize);
nFaces = rects->total;
for (int iface = 0; iface < (rects ? nFaces : 0); ++iface)
{
CvRect *r = (CvRect*)cvGetSeqElem(rects, iface);
bbox[0] = r->x;
bbox[1] = r->y;
bbox[2] = r->x + r->width;
bbox[3] = r->y + r->height;
flandmark_detect(input, bbox, model, landmarks);
// display landmarks
// cvRectangle(orig, cvPoint(bbox[0], bbox[1]), cvPoint(bbox[2], bbox[3]), CV_RGB(255,0,0) );
// cvRectangle(orig, cvPoint(model->bb[0], model->bb[1]), cvPoint(model->bb[2], model->bb[3]), CV_RGB(0,0,255) );
// cvCircle(orig, cvPoint((int)landmarks[0], (int)landmarks[1]), 3, CV_RGB(0, 0,255), CV_FILLED);
// for (int i = 2; i < 2*model->data.options.M; i += 2)
// {
// cvCircle(orig, cvPoint(int(landmarks[i]), int(landmarks[i+1])), 3, CV_RGB(255,0,0), CV_FILLED);
// }
}
// if (nFaces > 0)
// {
// DBG("Faces detected: %d; \n", nFaces);
// }
cvReleaseMemStorage(&storage);
cvReleaseImage(&input);
return nFaces;
}
用法:
//cv::Mat& img 这里就是需要检测人脸的图片,我这里都是用cv::Mat作为输入格式
IplImage frameRet = IplImage(img);
//这里存放检测到人脸8个特征点的坐标
double landmarks[MAX_LANDMARK_POINT * 2] = {0};
int faceNum = detectFaceInImage(&frameRet, img, landmarks);
if(faceNum > 0)
{
for (int i = 0; i < 2 * MAX_LANDMARK_POINT; i += 2)
{
//cv::circle(retImg, cvPoint(int(landmarks[i]), int(landmarks[i+1])), 2, CV_RGB(255,0,0), CV_FILLED);
}
}
//我这里只需要8个点坐标,frameRet其实没什么用