Skip to content

Commit fcdb241

Browse files
Added python bindings to facemark training code
1 parent 0915b7e commit fcdb241

13 files changed

+307
-81
lines changed

modules/face/include/opencv2/face/face_alignment.hpp

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace cv{
1010
namespace face{
11-
class CV_EXPORTS_W FacemarkKazemi : public Facemark
11+
class CV_EXPORTS_W FacemarkKazemi : public FacemarkTrain
1212
{
1313
public:
1414
struct CV_EXPORTS Params
@@ -35,25 +35,15 @@ class CV_EXPORTS_W FacemarkKazemi : public Facemark
3535
unsigned long num_test_splits;
3636
/// configfile stores the name of the file containing the values of training parameters
3737
String configfile;
38+
/// modelfile stores the name of the file containing the Kazemi model
39+
String modelfile;
40+
/// faceCascadefile stores the name of the file containing the face cascade model
41+
String faceCascadefile;
42+
/// scale to which all the images and the landmarks need to be scaled
43+
Size scale;
3844
};
3945
static Ptr<FacemarkKazemi> create(const FacemarkKazemi::Params &parameters = FacemarkKazemi::Params());
4046
virtual ~FacemarkKazemi();
41-
42-
/** @brief This function is used to train the model using gradient boosting to get a cascade of regressors
43-
*which can then be used to predict shape.
44-
*@param images A vector of type cv::Mat which stores the images which are used in training samples.
45-
*@param landmarks A vector of vectors of type cv::Point2f which stores the landmarks detected in a particular image.
46-
*@param scale A size of type cv::Size to which all images and landmarks have to be scaled to.
47-
*@param configfile A variable of type std::string which stores the name of the file storing parameters for training the model.
48-
*@param modelFilename A variable of type std::string which stores the name of the trained model file that has to be saved.
49-
*@returns A boolean value. The function returns true if the model is trained properly or false if it is not trained.
50-
*/
51-
virtual bool training(std::vector<Mat>& images, std::vector< std::vector<Point2f> >& landmarks,std::string configfile,Size scale,std::string modelFilename = "face_landmarks.dat")=0;
52-
53-
/// set the custom face detector
54-
virtual bool setFaceDetector(bool(*f)(InputArray , OutputArray, void*), void* userData)=0;
55-
/// get faces using the custom detector
56-
virtual bool getFaces(InputArray image, OutputArray faces)=0;
5747
};
5848

5949
}} // namespace

modules/face/include/opencv2/face/facemark.hpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,82 @@ class CV_EXPORTS_W Facemark : public virtual Algorithm
7676
CV_WRAP virtual bool fit( InputArray image,
7777
InputArray faces,
7878
OutputArrayOfArrays landmarks) = 0;
79+
80+
/** @brief Add one training sample to the trainer.
81+
82+
@param image Input image.
83+
@param landmarks The ground-truth of facial landmarks points corresponds to the image.
84+
85+
<B>Example of usage</B>
86+
@code
87+
String imageFiles = "../data/images_train.txt";
88+
String ptsFiles = "../data/points_train.txt";
89+
std::vector<String> images_train;
90+
std::vector<String> landmarks_train;
91+
92+
// load the list of dataset: image paths and landmark file paths
93+
loadDatasetList(imageFiles,ptsFiles,images_train,landmarks_train);
94+
95+
Mat image;
96+
std::vector<Point2f> facial_points;
97+
for(size_t i=0;i<images_train.size();i++){
98+
image = imread(images_train[i].c_str());
99+
loadFacePoints(landmarks_train[i],facial_points);
100+
facemark->addTrainingSample(image, facial_points);
101+
}
102+
@endcode
103+
104+
The contents in the training files should follows the standard format.
105+
Here are examples for the contents in these files.
106+
example of content in the images_train.txt
107+
@code
108+
/home/user/ibug/image_003_1.jpg
109+
/home/user/ibug/image_004_1.jpg
110+
/home/user/ibug/image_005_1.jpg
111+
/home/user/ibug/image_006.jpg
112+
@endcode
113+
114+
example of content in the points_train.txt
115+
@code
116+
/home/user/ibug/image_003_1.pts
117+
/home/user/ibug/image_004_1.pts
118+
/home/user/ibug/image_005_1.pts
119+
/home/user/ibug/image_006.pts
120+
@endcode
121+
122+
*/
123+
CV_WRAP virtual bool addTrainingSample(InputArray image, std::vector<Point2f> & landmarks)=0;
124+
125+
/** @brief Set parameters in the Facemark Instance.
126+
127+
@param face_cascade_name Path to the face cascade model
128+
@param facemark_model_name Path to the facemark model
129+
@param config_file_path Path to the Config file (only for FacemarkKazemi)
130+
@param scale vector of floats represent scale (only for FacemarkAAM, FacemarkKazemi)
131+
132+
<B>Example of usage</B>
133+
@code
134+
facemark->setParams(filename,modelfilename,configfile_name,scale)
135+
@endcode
136+
*/
137+
CV_WRAP virtual bool setParams(const String& face_cascade_name,const String& facemark_model_name, const String& config_file_path, InputArray scale)=0;
138+
139+
/** @brief Trains a Facemark algorithm using the given dataset.
140+
Before the training process, training samples should be added to the trainer
141+
using face::addTrainingSample function.
142+
143+
<B>Example of usage</B>
144+
@code
145+
FacemarkLBF::Params params;
146+
params.model_filename = "ibug68.model"; // filename to save the trained model
147+
Ptr<Facemark> facemark = FacemarkLBF::create(params);
148+
149+
// add training samples (see Facemark::addTrainingSample)
150+
151+
facemark->training();
152+
@endcode
153+
*/
154+
CV_WRAP virtual void training()=0;
79155
}; /* Facemark*/
80156

81157

modules/face/include/opencv2/face/facemarkAAM.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class CV_EXPORTS_W FacemarkAAM : public FacemarkTrain
6565
void write(FileStorage& /*fs*/) const;
6666

6767
std::string model_filename;
68+
std::string cascade_filename;
6869
int m;
6970
int n;
7071
int n_iter;

modules/face/include/opencv2/face/facemark_train.hpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ loadDatasetList(imageFiles,ptsFiles,images_train,landmarks_train);
9292
*/
9393
CV_EXPORTS_W bool loadDatasetList(String imageList,
9494
String annotationList,
95-
std::vector<String> & images,
96-
std::vector<String> & annotations);
95+
CV_OUT std::vector<String> & images,
96+
CV_OUT std::vector<String> & annotations);
9797

9898
/** @brief A utility to load facial landmark dataset from a single file.
9999
@@ -181,8 +181,8 @@ CV_EXPORTS_W bool loadTrainingData( String imageList, String groundTruth,
181181
* @param trainimages A vector of type cv::String which stores the name of images whose landmarks are tracked
182182
* @returns A boolean value. It returns true when it reads the data successfully and false otherwise
183183
*/
184-
CV_EXPORTS_W bool loadTrainingData(std::vector<String> filename,std::vector< std::vector<Point2f> >
185-
&trainlandmarks,std::vector<String> & trainimages);
184+
CV_EXPORTS_W bool loadTrainingData(std::vector<String> filename, CV_OUT std::vector< std::vector<Point2f> >
185+
&trainlandmarks, CV_OUT std::vector<String> & trainimages);
186186

187187
/** @brief A utility to load facial landmark information from a given file.
188188
@@ -305,14 +305,12 @@ class CV_EXPORTS_W FacemarkTrain : public Facemark
305305
@endcode
306306
307307
*/
308-
virtual bool addTrainingSample(InputArray image, InputArray landmarks)=0;
308+
virtual bool addTrainingSample(InputArray image, std::vector<Point2f> & landmarks)=0;
309309

310310
/** @brief Trains a Facemark algorithm using the given dataset.
311311
Before the training process, training samples should be added to the trainer
312312
using face::addTrainingSample function.
313313
314-
@param parameters Optional extra parameters (algorithm dependent).
315-
316314
<B>Example of usage</B>
317315
@code
318316
FacemarkLBF::Params params;
@@ -325,7 +323,7 @@ class CV_EXPORTS_W FacemarkTrain : public Facemark
325323
@endcode
326324
*/
327325

328-
virtual void training(void* parameters=0)=0;
326+
virtual void training()=0;
329327

330328
/** @brief Set a user defined face detector for the Facemark algorithm.
331329
@param detector The user defined face detector function

modules/face/samples/sample_train_landmark_detector.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,19 @@ int main(int argc,char** argv){
8989
face_cascade.load(cascade_name);
9090
FacemarkKazemi::Params params;
9191
params.configfile = configfile_name;
92+
params.faceCascadefile = cascade_name;
93+
params.modelfile = modelfile_name;
94+
params.scale = scale;
9295
Ptr<FacemarkKazemi> facemark = FacemarkKazemi::create(params);
96+
// std::vector<float> scale(2, 460);
97+
// facemark->setParams(cascade_name,modelfile_name,configfile_name,scale);
9398
facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
9499
//create a vector to store image names
95100
vector<String> imagenames;
96101
//create object to get landmarks
97-
vector< vector<Point2f> > trainlandmarks,Trainlandmarks;
102+
vector< vector<Point2f> > trainlandmarks;
98103
//gets landmarks and corresponding image names in both the vectors
99104
//vector to store images
100-
vector<Mat> trainimages;
101105
loadTrainingData(filenames,trainlandmarks,imagenames);
102106
for(unsigned long i=0;i<300;i++){
103107
string imgname = imagenames[i].substr(0, imagenames[i].size()-1);
@@ -107,11 +111,10 @@ int main(int argc,char** argv){
107111
cerr<<string("Image "+img+" not found\n.")<<endl;
108112
continue;
109113
}
110-
trainimages.push_back(src);
111-
Trainlandmarks.push_back(trainlandmarks[i]);
114+
facemark->addTrainingSample(src, trainlandmarks[i]);
112115
}
113116
cout<<"Got data"<<endl;
114-
facemark->training(trainimages,Trainlandmarks,configfile_name,scale,modelfile_name);
117+
facemark->training();
115118
cout<<"Training complete"<<endl;
116119
return 0;
117-
}
120+
}

modules/face/samples/sample_train_landmark_detector2.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,18 @@ int main(int argc,char** argv){
106106
face_cascade.load(cascade_name);
107107
FacemarkKazemi::Params params;
108108
params.configfile = configfile_name;
109+
params.faceCascadefile = cascade_name;
110+
params.modelfile = modelfile_name;
111+
params.scale = scale;
109112
Ptr<FacemarkKazemi> facemark = FacemarkKazemi::create(params);
113+
// std::vector<float> scale(2, 460);
114+
// facemark->setParams(cascade_name,modelfile_name,configfile_name,scale);
110115
facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
111116

112117
std::vector<String> images;
113118
std::vector<std::vector<Point2f> > facePoints;
114119
loadTrainingData(imagesList, annotations, images, facePoints, 0.0);
115120
//gets landmarks and corresponding image names in both the vectors
116-
vector<Mat> Trainimages;
117-
std::vector<std::vector<Point2f> > Trainlandmarks;
118-
//vector to store images
119121
Mat src;
120122
for(unsigned long i=0;i<images.size();i++){
121123
src = imread(images[i]);
@@ -124,11 +126,10 @@ int main(int argc,char** argv){
124126
cerr<<string("Image not found\n.Aborting...")<<endl;
125127
continue;
126128
}
127-
Trainimages.push_back(src);
128-
Trainlandmarks.push_back(facePoints[i]);
129+
facemark->addTrainingSample(src, facePoints[i]);
129130
}
130131
cout<<"Got data"<<endl;
131-
facemark->training(Trainimages,Trainlandmarks,configfile_name,scale,modelfile_name);
132+
facemark->training();
132133
cout<<"Training complete"<<endl;
133134
return 0;
134135
}

modules/face/src/face_alignment.cpp

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,78 @@ bool FacemarkKazemiImpl::setFaceDetector(FN_FaceDetector f, void* userData){
2727
}
2828
bool FacemarkKazemiImpl::getFaces(InputArray image, OutputArray faces)
2929
{
30-
CV_Assert(faceDetector);
30+
if (!faceDetector)
31+
{
32+
std::vector<Rect> faces_;
33+
defaultFaceDetector(image.getMat(), faces_);
34+
Mat(faces_).copyTo(faces);
35+
return true;
36+
}
3137
return faceDetector(image, faces, faceDetectorData);
3238
}
39+
bool FacemarkKazemiImpl::defaultFaceDetector(const Mat& image, std::vector<Rect>& faces){
40+
Mat gray;
41+
42+
faces.clear();
43+
44+
if (image.channels() > 1)
45+
{
46+
cvtColor(image, gray, COLOR_BGR2GRAY);
47+
}
48+
else
49+
{
50+
gray = image;
51+
}
52+
53+
equalizeHist(gray, gray);
54+
55+
if (face_cascade.empty())
56+
{
57+
{ /* check the cascade classifier file */
58+
std::ifstream infile;
59+
infile.open(params.faceCascadefile.c_str(), std::ios::in);
60+
if (!infile)
61+
CV_Error_(Error::StsBadArg, ("The cascade classifier model is not found: %s", params.faceCascadefile.c_str()));
62+
}
63+
face_cascade.load(params.faceCascadefile.c_str());
64+
CV_Assert(!face_cascade.empty());
65+
}
66+
face_cascade.detectMultiScale(gray, faces, 1.05, 2, CASCADE_SCALE_IMAGE, Size(30, 30) );
67+
return true;
68+
}
69+
bool FacemarkKazemiImpl::getData(void * items){
70+
CV_UNUSED(items);
71+
return false;
72+
}
73+
bool FacemarkKazemiImpl::addTrainingSample(InputArray image, std::vector<Point2f> & landmarks){
74+
std::vector<Point2f> & _landmarks = landmarks;
75+
training_images.push_back(image.getMat());
76+
training_facePoints.push_back(_landmarks);
77+
return true;
78+
}
79+
bool FacemarkKazemiImpl::setParams(const String& face_cascade_name,const String& facemark_model_name, const String& config_file_path, InputArray scale){
80+
if(face_cascade_name.empty() && facemark_model_name.empty() && config_file_path.empty() && scale.empty())
81+
{
82+
CV_Error_(Error::StsBadArg, ("face cascade name, facemark model name, config file and scale all are empty"));
83+
return false;
84+
}
85+
if(!face_cascade_name.empty())
86+
params.faceCascadefile = face_cascade_name;
87+
if(!facemark_model_name.empty())
88+
params.modelfile = facemark_model_name;
89+
if(!config_file_path.empty())
90+
params.configfile = config_file_path;
91+
92+
Mat scale_mat = scale.getMat();
93+
std::vector<float> _scale = scale_mat.reshape(1, scale_mat.rows);
94+
if(_scale.size() != 2){
95+
CV_Error(Error::StsBadArg, "Please set the scale argument properly");
96+
return false;
97+
}
98+
params.scale = Size(_scale[0], _scale[1]);
99+
CV_UNUSED(config_file_path);
100+
return true;
101+
}
33102
FacemarkKazemiImpl::FacemarkKazemiImpl(const FacemarkKazemi::Params& parameters) :
34103
faceDetector(NULL),
35104
faceDetectorData(NULL)
@@ -45,6 +114,9 @@ FacemarkKazemi::Params::Params(){
45114
//These variables are used for training data
46115
//These are initialised as described in the research paper
47116
//referenced above
117+
configfile = "";
118+
modelfile = "";
119+
faceCascadefile = "";
48120
cascade_depth = 15;
49121
tree_depth = 5;
50122
num_trees_per_cascade_level = 500;

modules/face/src/face_alignmentimpl.hpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ class FacemarkKazemiImpl : public FacemarkKazemi{
7373
void loadModel(String fs) CV_OVERRIDE;
7474
bool setFaceDetector(FN_FaceDetector f, void* userdata) CV_OVERRIDE;
7575
bool getFaces(InputArray image, OutputArray faces) CV_OVERRIDE;
76+
bool defaultFaceDetector(const Mat& image, std::vector<Rect>& faces);
77+
bool getData(void * items) CV_OVERRIDE;
7678
bool fit(InputArray image, InputArray faces, OutputArrayOfArrays landmarks ) CV_OVERRIDE;
77-
void training(String imageList, String groundTruth);
78-
bool training(vector<Mat>& images, vector< vector<Point2f> >& landmarks,string filename,Size scale,string modelFilename) CV_OVERRIDE;
79+
bool addTrainingSample(InputArray image, std::vector<Point2f> & landmarks) CV_OVERRIDE;
80+
bool setParams(const String& face_cascade_name,const String& facemark_model_name, const String& config_file_path, InputArray scale) CV_OVERRIDE;
81+
void training() CV_OVERRIDE;
7982
// Destructor for the class.
8083
virtual ~FacemarkKazemiImpl() CV_OVERRIDE;
8184

@@ -89,10 +92,14 @@ class FacemarkKazemiImpl : public FacemarkKazemi{
8992
float minmeany;
9093
float maxmeany;
9194
bool isModelLoaded;
95+
/* training data */
96+
std::vector<Mat> training_images;
97+
std::vector<std::vector<Point2f> > training_facePoints;
9298
/* meanshape This is a vector which stores the mean shape of all the images used in training*/
9399
std::vector<Point2f> meanshape;
94100
std::vector< std::vector<regtree> > loaded_forests;
95101
std::vector< std::vector<Point2f> > loaded_pixel_coordinates;
102+
CascadeClassifier face_cascade;
96103
FN_FaceDetector faceDetector;
97104
void* faceDetectorData;
98105
bool findNearestLandmarks(std::vector< std::vector<int> >& nearest);

modules/face/src/facemark.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ bool getFaces(InputArray image, OutputArray faces, CParams* params)
5151
return true;
5252
}
5353

54-
bool loadDatasetList(String imageList, String groundTruth, std::vector<String> & images, std::vector<String> & landmarks){
54+
bool loadDatasetList(String imageList, String groundTruth, CV_OUT std::vector<String> & images, CV_OUT std::vector<String> & landmarks){
5555
std::string line;
5656

5757
/*clear the output containers*/
@@ -215,8 +215,8 @@ bool getFacesHAAR(InputArray image, OutputArray faces, const String& face_cascad
215215
return true;
216216
}
217217

218-
bool loadTrainingData(vector<String> filename,vector< vector<Point2f> >
219-
& trainlandmarks,vector<String> & trainimages)
218+
bool loadTrainingData(vector<String> filename, CV_OUT vector< vector<Point2f> >
219+
& trainlandmarks, CV_OUT vector<String> & trainimages)
220220
{
221221
string img;
222222
vector<Point2f> temp;

0 commit comments

Comments
 (0)