目标
本教程将帮助你
- 创建一个 Facemark 对象。
- 为 facemark 算法设置一个用户定义的人脸检测器
- 训练该算法。
- 使用已训练模型从给定图像中检测人脸特征标记。
准备工作
在此教程继续之前,你应下载人脸特征标记检测数据集。我们建议你下载 helen 数据集,地址为 http://www.ifp.illinois.edu/~vuongle2/helen/ (警告!该算法需要大约 9GB RAM 才能训练此数据集)。
确保注释格式受 API 支持,注释文件中的内容应如下所示
version: 1
n_points: 68
{
212.716603 499.771793
230.232816 566.290071
...
}
接下来要做的就是分别制作 2 个文本文件,其中包含图像文件列表和注释文件列表。确保两个文件中的图像和注释的顺序匹配。此外,建议使用绝对路径,而不是相对路径。在 Linux 机器上创建文件列表的示例
ls $PWD/trainset/*.jpg > images_train.txt
ls $PWD/trainset/*.pts > annotation_train.txt
images_train.txt 中内容的示例
/home/user/helen/trainset/100032540_1.jpg
/home/user/helen/trainset/100040721_1.jpg
/home/user/helen/trainset/100040721_2.jpg
/home/user/helen/trainset/1002681492_1.jpg
annotation_train.txt 中内容的示例
/home/user/helen/trainset/100032540_1.pts
/home/user/helen/trainset/100040721_1.pts
/home/user/helen/trainset/100040721_2.pts
/home/user/helen/trainset/1002681492_1.pts
创建 facemark 对象
/*创建 facemark 实例*/
FacemarkLBF::Params params;
params.model_filename = "helen.model"; // 将使用此文件名保存已训练模型
Ptr<Facemark> facemark = FacemarkLBF::create(params);
设置自定义人脸检测器函数
首先,你需要创建你自己的一个人脸检测器函数,你可能还需要创建一个 struct
来保存自定义参数。或者,你可以在 myDetector
函数中硬编码这些参数。
struct Conf {
cv::String model_path;
double scaleFactor;
Conf(cv::String s, double d){
model_path = s;
scaleFactor = d;
face_detector.load(model_path);
};
CascadeClassifier face_detector;
};
bool myDetector(InputArray image, OutputArray faces, Conf *conf){
Mat gray;
if (image.channels() > 1)
cvtColor(image, gray, COLOR_BGR2GRAY);
else
gray = image.getMat().clone();
equalizeHist(gray, gray);
std::vector<Rect> faces_;
conf->face_cascade.detectMultiScale(gray, faces_, conf->scaleFactor, 2, CASCADE_SCALE_IMAGE, Size(30, 30) );
Mat(faces_).copyTo(faces);
return true;
}
以下代码片段演示如何将自定义检测器设置为 facemark 对象并使用它来检测面部。请记住,一些 facemark 对象可能会在训练过程中使用面部检测器。
Conf config("../data/lbpcascade_frontalface.xml", 1.4);
facemark->setFaceDetector(myDetector, &config); // 我们必须保证“config”对象的正确生存期
以下是使用用户定义的面部检测器函数检测面部的代码片段。
Mat img = imread("../data/himym3.jpg");
std::vector<cv::Rect> faces;
facemark->getFaces(img, faces, config);
for(int j=0;j<faces.size();j++){
cv::rectangle(img, faces[j], cv::Scalar(255,0,255));
}
imshow("result", img);
waitKey(0);
训练 facemark 对象
- 首先,你需要设置训练参数
params.n_landmarks = 68; // 地标点数
params.initShape_n = 10; // 增加数据增强的倍数
params.stages_n=5; // 精度阶段数量
params.tree_n=6; // 每个地标点的模型中的树数量
params.tree_depth=5; // 决策树的深度
facemark = FacemarkLBF::create(params);
- 然后,你需要从你准备好的数据集加载文件列表。
std::vector<String> images_train;
std::vector<String> landmarks_train;
loadDatasetList("images_train.txt","annotation_train.txt",images_train,landmarks_train);
- 下一步是将训练样本添加到 facemark 对象。
Mat image;
std::vector<Point2f> facial_points;
for(size_t i=0;i<images_train.size();i++){
image = imread(images_train[i].c_str());
loadFacePoints(landmarks_train[i],facial_points);
facemark->addTrainingSample(image, facial_points);
}
- 执行训练过程
/*训练算法*/
facemark->training();
使用训练好的模型来检测给定图像中的面部地标。
- 首先,加载经过训练的模型。你还可以从以下链接下载预训练的模型 https://raw.githubusercontent.com/kurnianggoro/GSOC2017/master/data/lbfmodel.yaml
facemark->loadModel(params.model_filename);
- 检测面部
facemark->getFaces(img, faces, config);
- 执行拟合适应过程
std::vector<std::vector<Point2f> > landmarks;
facemark->fit(img, faces, landmarks);
- 显示结果
for(int j=0;j<faces.size();j++){
face::drawFacemarks(img, landmarks[j], Scalar(0,0,255));
}
imshow("result", img);
waitKey(0);