Training face landmark detector
This demo helps to train your own face landmark detector. The user should provide the list of training images accompanied by their corresponding landmarks location in separated files.
Examples of datasets are available at https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/.
We suggest you to download the HELEN dataset which can be retrieved at https://ibug.doc.ic.ac.uk/download/annotations/helen.zip (Caution: The algorithm requires considerable RAM to train on this dataset).
Example of contents for images.txt file:
/data/helen/100032540_1.jpg /data/helen/100040721_1.jpg /data/helen/100040721_2.jpg ...
Example of contents for annotations.txt file:
/data/helen/100032540_1.pts /data/helen/100040721_1.pts /data/helen/100040721_2.pts ...
where a .pts file contains the position of each face landmark. Example of contents for .pts files:
version: 1 n_points: 68 { 212.716603 499.771793 230.232816 566.290071 ... }
For a description of training parameters used in configFile, see the demo facemark_kazemi_train_config_demo.m.
You can also download a pre-trained model face_landmark_model.dat, see the demo facemark_kazemi_detect_img_demo. (that way you can skip training and simply load the model).
Sources:
Contents
Options
% [INPUT] path of a text file contains the list of paths to all training images imgList = fullfile(mexopencv.root(),'test','facemark','helen','images.lst'); assert(exist(imgList, 'file') == 2, 'missing images list file'); % [INPUT] path of a text file contains the list of paths to all annotations files ptsList = fullfile(mexopencv.root(),'test','facemark','helen','annotations.lst'); assert(exist(ptsList, 'file') == 2, 'missing annotations list file'); % [INPUT] path to configuration xml file containing parameters for training % https://github.com/opencv/opencv_contrib/raw/3.4.0/modules/face/samples/sample_config_file.xml configFile = fullfile(mexopencv.root(),'test','facemark','config.xml'); assert(exist(configFile, 'file') == 2, 'missing train config file'); % [OUTPUT] path for saving the trained model modelFile = fullfile(tempdir(), 'model_kazemi.dat'); % [INPUT] path to the cascade xml file for the face detector xmlFace = fullfile(mexopencv.root(),'test','lbpcascade_frontalface.xml'); download_classifier_xml(xmlFace); % name of user-defined face detector function faceDetectFcn = 'myFaceDetector'; assert(exist([faceDetectFcn '.m'], 'file') == 2, 'missing face detect function'); % width/height which you want all images to get to scale the annotations. % larger images are slower to process scale = [460 460];
Data
% load names of images and annotation files disp('Loading data...') [imgFiles, ptsFiles] = cv.Facemark.loadDatasetList(imgList, ptsList); % load images and their corresponding landmarks imgs = cell(size(imgFiles)); pts = cell(size(ptsFiles)); for i=1:numel(imgFiles) imgs{i} = cv.imread(imgFiles{i}); pts{i} = cv.Facemark.loadFacePoints(ptsFiles{i}); end
Loading data...
Init
create instance of the face landmark detection class, and set the face detector function
obj = cv.FacemarkKazemi('ConfigFile',configFile);
obj.setFaceDetector(faceDetectFcn);
Train
perform training
disp('Training...') tic success = obj.training(imgs, pts, configFile, scale, 'ModelFilename',modelFile); toc if success disp('Training successful') else disp('Training failed') end
Training... Elapsed time is 78.617735 seconds. Training successful
In the above call, scale is passed to scale all images and their corresponding landmarks, as it takes greater time to process large images. After scaling data it calculates mean shape of the data which is used as initial shape while training. It trains the model and stores the trained model file with the specified filename. As the training starts, you will see something like this:
The error rate on trained images depends on the number of images used for training:
The error rate on test images depends on the number of images used for training:
Helper functions
function download_classifier_xml(fname) if exist(fname, 'file') ~= 2 % attempt to download trained Haar/LBP/HOG classifier from Github url = 'https://cdn.rawgit.com/opencv/opencv/3.4.0/data/'; [~, f, ext] = fileparts(fname); if strncmpi(f, 'haarcascade_', length('haarcascade_')) url = [url, 'haarcascades/']; elseif strncmpi(f, 'lbpcascade_', length('lbpcascade_')) url = [url, 'lbpcascades/']; elseif strncmpi(f, 'hogcascade_', length('hogcascade_')) url = [url, 'hogcascades/']; else error('File not found'); end urlwrite([url f ext], fname); end end % The facemark API provides the functionality to the user to use their own % face detector. The code below implements a sample face detector. This % function must be saved in its own M-function to be used by the facemark API. function faces = myFaceDetector(img) persistent obj if isempty(obj) obj = cv.CascadeClassifier(); obj.load(xmlFace); end if size(img,3) > 1 gray = cv.cvtColor(img, 'RGB2GRAY'); else gray = img; end gray = cv.equalizeHist(gray); faces = obj.detect(gray, 'ScaleFactor',1.4, 'MinNeighbors',2, ... 'ScaleImage',true, 'MinSize',[30 30]); end