在之前的博客中,我们已经介绍了基于libfacedetection的人脸定位。该项目也实现了初步的人脸特征点定位(5个点,包括眼睛,鼻尖和嘴角)。在一些应用中,我们需要更加精确的人脸特征点来指导我们实现诸如建模,配准,分区等操作,仅仅五个特征点是不能满足需求的。因此,更加精确的人脸特征点定位算法需要被引入。这里,我门介绍一个经典的,容易配置的人脸特征点定位算法,能够满足大部分特征点定位的应用需求,即DEST(Deformable Shape Tracking)
关联Paper:One Millisecond Face Alignment with an Ensemble of Regression Trees
Github:GitHub - cheind/dest: One Millisecond Deformable Shape Tracking Library (DEST)
项目链接:Face Alignment
DEST是一个比较老的方法,我之所以选择DEST做我项目中的人脸特征点提取模块,主要是因为其配置比较简单,且性能不错。接下来我们介绍DEST编译的具体步骤。
首先从github上下载DEST项目,之后使用cmake进行初始配置,注意,这里需要提前安装好opencv,我是用的是3.4.6版本。
在configuring done之后,我们能看到一些报警信息。这时,我们需要勾选后两个选项,然后把eigen的路径配置好,如下:
之后点击生成,完成cmake的初始配置。
接下来就是对dest项目进行编译, 打开你在cmake中设定的项目路径,找到deformable-shape-tracking.sln,用VS打开。
build ALL_BUILD,完成编译
将dest/core/路径下的config.h文件拷贝到源文件路径dest-master/inc/dest/core下
这里需要下载一个bin文件(dest_tracker_VJ_ibug.bin),用于存储人脸特征点定位的预训练模型。项目作者已经提供了最新的下载:Releases · cheind/dest · GitHub。我存储在dest-master\etc\cv路径下,和classifier_frontalface_alt2.xml放在一起。这两个文件都是用来载入dest人脸检测模型的。
到这里,前期的准备工作基本就完成了。
到这一步,就是把之前编译好的dest项目配置在你希望使用其功能的VS项目中即可。你需要提前配置好opencv和eigen。
include路径:
dest-master\inc
dest-master\ext
Lib路径:
Lib路径在你编译好的dest项目中的debug文件夹下,如下图:
E:\project\FacialLandmarksDetection\dest\Debug
Linker Input:
dest.lib
到这里,VS项目的基本配置就完成了。
参考examples的代码,我给出一个具体的实现:
pathFaceDetector 用于载入xml。
pathTracker 用于载入在之前我们已经下载的bin文件。
pathImage 载入我们需要探查的图片路径。
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char** argv)
{
std::string pathFaceDetector = "../dest-master/etc/cv/classifier_frontalface_alt2.xml";
std::string pathTracker = "../dest-master/etc/cv/dest_tracker_VJ_ibug.bin";
std::string pathImage = "../Figure/instance.jpg";
dest::face::FaceDetector fd;
if (!fd.loadClassifiers(pathFaceDetector)) {
std::cout << "Failed to load classifiers." << std::endl;
return 0;
}
dest::core::Tracker t;
if (!t.load(pathTracker)) {
std::cout << "Failed to load tracker." << std::endl;
return 0;
}
cv::Mat imgCV = cv::imread(pathImage, cv::IMREAD_GRAYSCALE);
cv::Mat imgCV_show = cv::imread(pathImage);
if (imgCV.empty()) {
std::cout << "Failed to load image " << pathImage << std::endl;
return 0;
}
dest::core::Image img;
dest::util::toDest(imgCV, img);
dest::core::Rect r;
if (!fd.detectSingleFace(img, r)) {
std::cout << "Failed to detect face" << std::endl;
return 0;
}
// Default inverse shape normalization. Needs to be equivalent to training.
dest::core::ShapeTransform shapeToImage = dest::core::estimateSimilarityTransform(dest::core::unitRectangle(), r);
std::vector steps;
dest::core::Shape s = t.predict(img, shapeToImage, &steps);
cv::Scalar color = cv::Scalar(0, 255, 0);
for (dest::core::Shape::Index i = 0; i < s.cols(); ++i) {
cv::circle(imgCV_show, cv::Point2f(s(0, i), s(1, i)), 2.f, color, -1, CV_AA);
}
cv::imshow("prediction", imgCV_show);
int key = cv::waitKey();
return 0;
}
结果:
如果你看到这里的话,希望能给点个赞,万分感谢。