Commit fbaa1a16 authored by Alexander Alekhin's avatar Alexander Alekhin

Merge pull request #1592 from berak:landmarks

parents d5fe83ee 51e96d89
...@@ -375,6 +375,7 @@ protected: ...@@ -375,6 +375,7 @@ protected:
#include "opencv2/face/facerec.hpp" #include "opencv2/face/facerec.hpp"
#include "opencv2/face/facemark.hpp" #include "opencv2/face/facemark.hpp"
#include "opencv2/face/facemark_train.hpp"
#include "opencv2/face/facemarkLBF.hpp" #include "opencv2/face/facemarkLBF.hpp"
#include "opencv2/face/facemarkAAM.hpp" #include "opencv2/face/facemarkAAM.hpp"
#include "opencv2/face/face_alignment.hpp" #include "opencv2/face/face_alignment.hpp"
......
...@@ -4,11 +4,11 @@ ...@@ -4,11 +4,11 @@
#ifndef __OPENCV_FACE_ALIGNMENT_HPP__ #ifndef __OPENCV_FACE_ALIGNMENT_HPP__
#define __OPENCV_FACE_ALIGNMENT_HPP__ #define __OPENCV_FACE_ALIGNMENT_HPP__
#include "facemark.hpp" #include "opencv2/face/facemark_train.hpp"
namespace cv{ namespace cv{
namespace face{ namespace face{
class CV_EXPORTS_W FacemarkKazemi : public Algorithm class CV_EXPORTS_W FacemarkKazemi : public Facemark
{ {
public: public:
struct CV_EXPORTS Params struct CV_EXPORTS Params
...@@ -39,8 +39,6 @@ public: ...@@ -39,8 +39,6 @@ public:
static Ptr<FacemarkKazemi> create(const FacemarkKazemi::Params &parameters = FacemarkKazemi::Params()); static Ptr<FacemarkKazemi> create(const FacemarkKazemi::Params &parameters = FacemarkKazemi::Params());
virtual ~FacemarkKazemi(); virtual ~FacemarkKazemi();
/// @brief training the facemark model, input are the file names of image list and landmark annotation
virtual void training(String imageList, String groundTruth)=0;
/** @brief This function is used to train the model using gradient boosting to get a cascade of regressors /** @brief This function is used to train the model using gradient boosting to get a cascade of regressors
*which can then be used to predict shape. *which can then be used to predict shape.
*@param images A vector of type cv::Mat which stores the images which are used in training samples. *@param images A vector of type cv::Mat which stores the images which are used in training samples.
...@@ -51,16 +49,7 @@ public: ...@@ -51,16 +49,7 @@ public:
*@returns A boolean value. The function returns true if the model is trained properly or false if it is not trained. *@returns A boolean value. The function returns true if the model is trained properly or false if it is not trained.
*/ */
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; 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;
/** @brief This function is used to load the trained model..
*@param filename A variable of type cv::String which stores the name of the file in which trained model is stored.
*/
virtual void loadModel(String filename)=0;
/** @brief This functions retrieves a centered and scaled face shape, according to the bounding rectangle.
*@param image A variable of type cv::InputArray which stores the image whose landmarks have to be found
*@param faces A variable of type cv::InputArray which stores the bounding boxes of faces found in a given image.
*@param landmarks A variable of type cv::InputOutputArray which stores the landmarks of all the faces found in the image
*/
virtual bool fit( InputArray image, InputArray faces, InputOutputArray landmarks )=0;//!< from many ROIs
/// set the custom face detector /// set the custom face detector
virtual bool setFaceDetector(bool(*f)(InputArray , OutputArray, void*), void* userData)=0; virtual bool setFaceDetector(bool(*f)(InputArray , OutputArray, void*), void* userData)=0;
/// get faces using the custom detector /// get faces using the custom detector
......
...@@ -18,323 +18,37 @@ Mentor: Delia Passalacqua ...@@ -18,323 +18,37 @@ Mentor: Delia Passalacqua
- The Facemark API - The Facemark API
*/ */
#include "opencv2/face.hpp" #include "opencv2/core.hpp"
#include "opencv2/objdetect.hpp"
#include <vector> #include <vector>
#include <string>
namespace cv { namespace cv {
namespace face { namespace face {
//! @addtogroup face
//! @{
typedef bool(*FN_FaceDetector)(InputArray, OutputArray, void* userData);
struct CParams{
String cascade; //!< the face detector
double scaleFactor; //!< Parameter specifying how much the image size is reduced at each image scale.
int minNeighbors; //!< Parameter specifying how many neighbors each candidate rectangle should have to retain it.
Size minSize; //!< Minimum possible object size.
Size maxSize; //!< Maximum possible object size.
CV_EXPORTS CParams(
String cascade_model,
double sf = 1.1,
int minN = 3,
Size minSz = Size(30, 30),
Size maxSz = Size()
);
CascadeClassifier face_cascade;
};
/** @brief Default face detector
This function is mainly utilized by the implementation of a Facemark Algorithm.
End users are advised to use function Facemark::getFaces which can be manually defined
and circumvented to the algorithm by Facemark::setFaceDetector.
@param image The input image to be processed.
@param faces Output of the function which represent region of interest of the detected faces.
Each face is stored in cv::Rect container.
@param params detector parameters
<B>Example of usage</B>
@code
std::vector<cv::Rect> faces;
CParams params("haarcascade_frontalface_alt.xml");
cv::face::getFaces(frame, faces, &params);
for(int j=0;j<faces.size();j++){
cv::rectangle(frame, faces[j], cv::Scalar(255,0,255));
}
cv::imshow("detection", frame);
@endcode
*/
CV_EXPORTS bool getFaces(InputArray image, OutputArray faces, CParams* params);
CV_EXPORTS_W bool getFacesHAAR(InputArray image, OutputArray faces, const String& face_cascade_name);
/** @brief A utility to load list of paths to training image and annotation file.
@param imageList The specified file contains paths to the training images.
@param annotationList The specified file contains paths to the training annotations.
@param images The loaded paths of training images.
@param annotations The loaded paths of annotation files.
Example of usage:
@code
String imageFiles = "images_path.txt";
String ptsFiles = "annotations_path.txt";
std::vector<String> images_train;
std::vector<String> landmarks_train;
loadDatasetList(imageFiles,ptsFiles,images_train,landmarks_train);
@endcode
*/
CV_EXPORTS_W bool loadDatasetList(String imageList,
String annotationList,
std::vector<String> & images,
std::vector<String> & annotations);
/** @brief A utility to load facial landmark dataset from a single file.
@param filename The filename of a file that contains the dataset information.
Each line contains the filename of an image followed by
pairs of x and y values of facial landmarks points separated by a space.
Example
@code
/home/user/ibug/image_003_1.jpg 336.820955 240.864510 334.238298 260.922709 335.266918 ...
/home/user/ibug/image_005_1.jpg 376.158428 230.845712 376.736984 254.924635 383.265403 ...
@endcode
@param images A vector where each element represent the filename of image in the dataset.
Images are not loaded by default to save the memory.
@param facePoints The loaded landmark points for all training data.
@param delim Delimiter between each element, the default value is a whitespace.
@param offset An offset value to adjust the loaded points.
<B>Example of usage</B>
@code
cv::String imageFiles = "../data/images_train.txt";
cv::String ptsFiles = "../data/points_train.txt";
std::vector<String> images;
std::vector<std::vector<Point2f> > facePoints;
loadTrainingData(imageFiles, ptsFiles, images, facePoints, 0.0f);
@endcode
*/
CV_EXPORTS_W bool loadTrainingData( String filename , std::vector<String> & images,
OutputArray facePoints,
char delim = ' ', float offset = 0.0f);
/** @brief A utility to load facial landmark information from the dataset.
@param imageList A file contains the list of image filenames in the training dataset.
@param groundTruth A file contains the list of filenames
where the landmarks points information are stored.
The content in each file should follow the standard format (see face::loadFacePoints).
@param images A vector where each element represent the filename of image in the dataset.
Images are not loaded by default to save the memory.
@param facePoints The loaded landmark points for all training data.
@param offset An offset value to adjust the loaded points.
<B>Example of usage</B>
@code
cv::String imageFiles = "../data/images_train.txt";
cv::String ptsFiles = "../data/points_train.txt";
std::vector<String> images;
std::vector<std::vector<Point2f> > facePoints;
loadTrainingData(imageFiles, ptsFiles, images, facePoints, 0.0f);
@endcode
example of content in the images_train.txt
@code
/home/user/ibug/image_003_1.jpg
/home/user/ibug/image_004_1.jpg
/home/user/ibug/image_005_1.jpg
/home/user/ibug/image_006.jpg
@endcode
example of content in the points_train.txt
@code
/home/user/ibug/image_003_1.pts
/home/user/ibug/image_004_1.pts
/home/user/ibug/image_005_1.pts
/home/user/ibug/image_006.pts
@endcode
*/
CV_EXPORTS_W bool loadTrainingData( String imageList, String groundTruth,
std::vector<String> & images,
OutputArray facePoints,
float offset = 0.0f);
/** @brief This function extracts the data for training from .txt files which contains the corresponding image name and landmarks.
*The first file in each file should give the path of the image whose
*landmarks are being described in the file. Then in the subsequent
*lines there should be coordinates of the landmarks in the image
*i.e each line should be of the form x,y
*where x represents the x coordinate of the landmark and y represents
*the y coordinate of the landmark.
*
*For reference you can see the files as provided in the
*<a href="http://www.ifp.illinois.edu/~vuongle2/helen/">HELEN dataset</a>
*
* @param filename A vector of type cv::String containing name of the .txt files.
* @param trainlandmarks A vector of type cv::Point2f that would store shape or landmarks of all images.
* @param trainimages A vector of type cv::String which stores the name of images whose landmarks are tracked
* @returns A boolean value. It returns true when it reads the data successfully and false otherwise
*/
CV_EXPORTS_W bool loadTrainingData(std::vector<String> filename,std::vector< std::vector<Point2f> >
&trainlandmarks,std::vector<String> & trainimages);
/** @brief A utility to load facial landmark information from a given file.
@param filename The filename of file contains the facial landmarks data.
@param points The loaded facial landmark points.
@param offset An offset value to adjust the loaded points.
<B>Example of usage</B>
@code
std::vector<Point2f> points;
face::loadFacePoints("filename.txt", points, 0.0f);
@endcode
The annotation file should follow the default format which is
@code
version: 1
n_points: 68
{
212.716603 499.771793
230.232816 566.290071
...
}
@endcode
where n_points is the number of points considered
and each point is represented as its position in x and y.
*/
CV_EXPORTS_W bool loadFacePoints( String filename, OutputArray points,
float offset = 0.0f);
/** @brief Utility to draw the detected facial landmark points
@param image The input image to be processed.
@param points Contains the data of points which will be drawn.
@param color The color of points in BGR format represented by cv::Scalar.
<B>Example of usage</B>
@code
std::vector<Rect> faces;
std::vector<std::vector<Point2f> > landmarks;
facemark->getFaces(img, faces);
facemark->fit(img, faces, landmarks);
for(int j=0;j<rects.size();j++){
face::drawFacemarks(frame, landmarks[j], Scalar(0,0,255));
}
@endcode
*/
CV_EXPORTS_W void drawFacemarks( InputOutputArray image, InputArray points,
Scalar color = Scalar(255,0,0));
/** @brief Abstract base class for all facemark models /** @brief Abstract base class for all facemark models
All facemark models in OpenCV are derived from the abstract base class Facemark, which
provides a unified access to all facemark algorithms in OpenCV.
To utilize this API in your program, please take a look at the @ref tutorial_table_of_content_facemark To utilize this API in your program, please take a look at the @ref tutorial_table_of_content_facemark
### Description ### Description
Facemark is a base class which provides universal access to any specific facemark algorithm. Facemark is a base class which provides universal access to any specific facemark algorithm.
Therefore, the users should declare a desired algorithm before they can use it in their application. Therefore, the users should declare a desired algorithm before they can use it in their application.
Here is an example on how to declare facemark algorithm: Here is an example on how to declare a facemark algorithm:
@code @code
// Using Facemark in your code: // Using Facemark in your code:
Ptr<Facemark> facemark = FacemarkLBF::create(); Ptr<Facemark> facemark = createFacemarkLBF();
@endcode @endcode
The typical pipeline for facemark detection is listed as follows: The typical pipeline for facemark detection is as follows:
- (Non-mandatory) Set a user defined face detection using Facemark::setFaceDetector.
The facemark algorithms are desgined to fit the facial points into a face.
Therefore, the face information should be provided to the facemark algorithm.
Some algorithms might provides a default face recognition function.
However, the users might prefer to use their own face detector to obtains the best possible detection result.
- (Non-mandatory) Training the model for a specific algorithm using Facemark::training.
In this case, the model should be automatically saved by the algorithm.
If the user already have a trained model, then this part can be omitted.
- Load the trained model using Facemark::loadModel. - Load the trained model using Facemark::loadModel.
- Perform the fitting via the Facemark::fit. - Perform the fitting on an image via Facemark::fit.
*/ */
class CV_EXPORTS_W Facemark : public virtual Algorithm class CV_EXPORTS_W Facemark : public virtual Algorithm
{ {
public: public:
virtual void read( const FileNode& fn ) CV_OVERRIDE = 0;
virtual void write( FileStorage& fs ) const CV_OVERRIDE = 0;
/** @brief Add one training sample to the trainer.
@param image Input image.
@param landmarks The ground-truth of facial landmarks points corresponds to the image.
<B>Example of usage</B>
@code
String imageFiles = "../data/images_train.txt";
String ptsFiles = "../data/points_train.txt";
std::vector<String> images_train;
std::vector<String> landmarks_train;
// load the list of dataset: image paths and landmark file paths
loadDatasetList(imageFiles,ptsFiles,images_train,landmarks_train);
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);
}
@endcode
The contents in the training files should follows the standard format.
Here are examples for the contents in these files.
example of content in the images_train.txt
@code
/home/user/ibug/image_003_1.jpg
/home/user/ibug/image_004_1.jpg
/home/user/ibug/image_005_1.jpg
/home/user/ibug/image_006.jpg
@endcode
example of content in the points_train.txt
@code
/home/user/ibug/image_003_1.pts
/home/user/ibug/image_004_1.pts
/home/user/ibug/image_005_1.pts
/home/user/ibug/image_006.pts
@endcode
*/
virtual bool addTrainingSample(InputArray image, InputArray landmarks)=0;
/** @brief Trains a Facemark algorithm using the given dataset.
Before the training process, training samples should be added to the trainer
using face::addTrainingSample function.
@param parameters Optional extra parameters (algorithm dependent).
<B>Example of usage</B>
@code
FacemarkLBF::Params params;
params.model_filename = "ibug68.model"; // filename to save the trained model
Ptr<Facemark> facemark = FacemarkLBF::create(params);
// add training samples (see Facemark::addTrainingSample)
facemark->training();
@endcode
*/
virtual void training(void* parameters=0)=0;
/** @brief A function to load the trained model before the fitting process. /** @brief A function to load the trained model before the fitting process.
@param model A string represent the filename of a trained model. @param model A string represent the filename of a trained model.
<B>Example of usage</B> <B>Example of usage</B>
...@@ -342,16 +56,14 @@ public: ...@@ -342,16 +56,14 @@ public:
facemark->loadModel("../data/lbf.model"); facemark->loadModel("../data/lbf.model");
@endcode @endcode
*/ */
virtual void loadModel(String model)=0; CV_WRAP virtual void loadModel( String model ) = 0;
// virtual void saveModel(String fs)=0; // virtual void saveModel(String fs)=0;
/** @brief Trains a Facemark algorithm using the given dataset. /** @brief Detect facial landmarks from an image.
@param image Input image. @param image Input image.
@param faces Output of the function which represent region of interest of the detected faces. @param faces Output of the function which represent region of interest of the detected faces.
Each face is stored in cv::Rect container. Each face is stored in cv::Rect container.
@param landmarks The detected landmark points for each faces. @param landmarks The detected landmark points for each faces.
@param config Algorithm specific for running time parameters.
<B>Example of usage</B> <B>Example of usage</B>
@code @code
...@@ -360,74 +72,24 @@ public: ...@@ -360,74 +72,24 @@ public:
std::vector<std::vector<Point2f> > landmarks; std::vector<std::vector<Point2f> > landmarks;
facemark->fit(image, faces, landmarks); facemark->fit(image, faces, landmarks);
@endcode @endcode
TODO remove "config" from here
*/
virtual bool fit( InputArray image,
InputArray faces,
InputOutputArray landmarks,
void * config = 0)=0;
/** @brief Set a user defined face detector for the Facemark algorithm.
@param detector The user defined face detector function
@param userData Detector parameters
<B>Example of usage</B>
@code
MyDetectorParameters detectorParameters(...);
facemark->setFaceDetector(myDetector, &detectorParameters);
@endcode
Example of a user defined face detector
@code
bool myDetector( InputArray image, OutputArray faces, void* userData)
{
MyDetectorParameters* params = (MyDetectorParameters*)userData;
// -------- do something --------
}
@endcode
TODO Lifetime of detector parameters is uncontrolled. Rework interface design to "Ptr<FaceDetector>".
*/ */
virtual bool setFaceDetector(FN_FaceDetector detector, void* userData = 0)=0; CV_WRAP virtual bool fit( InputArray image,
InputArray faces,
OutputArrayOfArrays landmarks ) = 0;
}; /* Facemark*/
/** @brief Detect faces from a given image using default or user defined face detector.
Some Algorithm might not provide a default face detector.
@param image Input image. //! construct an AAM facemark detector
@param faces Output of the function which represent region of interest of the detected faces. Each face is stored in cv::Rect container. CV_EXPORTS_W Ptr<Facemark> createFacemarkAAM();
<B>Example of usage</B> //! construct an LBF facemark detector
@code CV_EXPORTS_W Ptr<Facemark> createFacemarkLBF();
std::vector<cv::Rect> faces;
facemark->getFaces(img, faces);
for(int j=0;j<faces.size();j++){
cv::rectangle(img, faces[j], cv::Scalar(255,0,255));
}
@endcode
*/
virtual bool getFaces(InputArray image, OutputArray faces)=0;
/** @brief Get data from an algorithm //! construct a Kazemi facemark detector
CV_EXPORTS_W Ptr<Facemark> createFacemarkKazemi();
@param items The obtained data, algorithm dependent.
<B>Example of usage</B> } // face
@code } // cv
Ptr<FacemarkAAM> facemark = FacemarkAAM::create();
facemark->loadModel("AAM.yml");
FacemarkAAM::Data data;
facemark->getData(&data);
std::vector<Point2f> s0 = data.s0;
cout<<s0<<endl;
@endcode
*/
virtual bool getData(void * items=0)=0; // FIXIT
}; /* Facemark*/
//! @}
} /* namespace face */
} /* namespace cv */
#endif //__OPENCV_FACELANDMARK_HPP__ #endif //__OPENCV_FACELANDMARK_HPP__
...@@ -37,14 +37,14 @@ Mentor: Delia Passalacqua ...@@ -37,14 +37,14 @@ Mentor: Delia Passalacqua
#ifndef __OPENCV_FACEMARK_AAM_HPP__ #ifndef __OPENCV_FACEMARK_AAM_HPP__
#define __OPENCV_FACEMARK_AAM_HPP__ #define __OPENCV_FACEMARK_AAM_HPP__
#include "opencv2/face/facemark.hpp" #include "opencv2/face/facemark_train.hpp"
namespace cv { namespace cv {
namespace face { namespace face {
//! @addtogroup face //! @addtogroup face
//! @{ //! @{
class CV_EXPORTS_W FacemarkAAM : public Facemark class CV_EXPORTS_W FacemarkAAM : public FacemarkTrain
{ {
public: public:
struct CV_EXPORTS Params struct CV_EXPORTS Params
...@@ -105,8 +105,6 @@ public: ...@@ -105,8 +105,6 @@ public:
*/ */
struct CV_EXPORTS Model struct CV_EXPORTS Model
{ {
int npts; //!< unused delete
int max_n; //!< unused delete
std::vector<float>scales; std::vector<float>scales;
//!< defines the scales considered to build the model //!< defines the scales considered to build the model
...@@ -147,7 +145,11 @@ public: ...@@ -147,7 +145,11 @@ public:
}; };
//!< initializer //! overload with additional Config structures
virtual bool fitConfig( InputArray image, InputArray roi, OutputArrayOfArrays _landmarks, const std::vector<Config> &runtime_params ) = 0;
//! initializer
static Ptr<FacemarkAAM> create(const FacemarkAAM::Params &parameters = FacemarkAAM::Params() ); static Ptr<FacemarkAAM> create(const FacemarkAAM::Params &parameters = FacemarkAAM::Params() );
virtual ~FacemarkAAM() {} virtual ~FacemarkAAM() {}
......
...@@ -37,7 +37,7 @@ Mentor: Delia Passalacqua ...@@ -37,7 +37,7 @@ Mentor: Delia Passalacqua
#ifndef __OPENCV_FACEMARK_LBF_HPP__ #ifndef __OPENCV_FACEMARK_LBF_HPP__
#define __OPENCV_FACEMARK_LBF_HPP__ #define __OPENCV_FACEMARK_LBF_HPP__
#include "opencv2/face/facemark.hpp" #include "opencv2/face/facemark_train.hpp"
namespace cv { namespace cv {
namespace face { namespace face {
...@@ -45,7 +45,7 @@ namespace face { ...@@ -45,7 +45,7 @@ namespace face {
//! @addtogroup face //! @addtogroup face
//! @{ //! @{
class CV_EXPORTS_W FacemarkLBF : public Facemark class CV_EXPORTS_W FacemarkLBF : public FacemarkTrain
{ {
public: public:
struct CV_EXPORTS Params struct CV_EXPORTS Params
...@@ -99,8 +99,8 @@ public: ...@@ -99,8 +99,8 @@ public:
~BBox(); ~BBox();
BBox(double x, double y, double w, double h); BBox(double x, double y, double w, double h);
cv::Mat project(const cv::Mat &shape) const; Mat project(const Mat &shape) const;
cv::Mat reproject(const cv::Mat &shape) const; Mat reproject(const Mat &shape) const;
double x, y; double x, y;
double x_center, y_center; double x_center, y_center;
......
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
/*
This file was part of GSoC Project: Facemark API for OpenCV
Final report: https://gist.github.com/kurnianggoro/74de9121e122ad0bd825176751d47ecc
Student: Laksono Kurnianggoro
Mentor: Delia Passalacqua
*/
#ifndef __OPENCV_FACELANDMARKTRAIN_HPP__
#define __OPENCV_FACELANDMARKTRAIN_HPP__
/**
@defgroup face Face Analysis
- @ref tutorial_table_of_content_facemark
- The Facemark API
*/
#include "opencv2/face/facemark.hpp"
#include "opencv2/objdetect.hpp"
#include <vector>
#include <string>
namespace cv {
namespace face {
//! @addtogroup face
//! @{
typedef bool(*FN_FaceDetector)(InputArray, OutputArray, void* userData);
struct CParams{
String cascade; //!< the face detector
double scaleFactor; //!< Parameter specifying how much the image size is reduced at each image scale.
int minNeighbors; //!< Parameter specifying how many neighbors each candidate rectangle should have to retain it.
Size minSize; //!< Minimum possible object size.
Size maxSize; //!< Maximum possible object size.
CV_EXPORTS CParams(
String cascade_model,
double sf = 1.1,
int minN = 3,
Size minSz = Size(30, 30),
Size maxSz = Size()
);
CascadeClassifier face_cascade;
};
/** @brief Default face detector
This function is mainly utilized by the implementation of a Facemark Algorithm.
End users are advised to use function Facemark::getFaces which can be manually defined
and circumvented to the algorithm by Facemark::setFaceDetector.
@param image The input image to be processed.
@param faces Output of the function which represent region of interest of the detected faces.
Each face is stored in cv::Rect container.
@param params detector parameters
<B>Example of usage</B>
@code
std::vector<cv::Rect> faces;
CParams params("haarcascade_frontalface_alt.xml");
cv::face::getFaces(frame, faces, &params);
for(int j=0;j<faces.size();j++){
cv::rectangle(frame, faces[j], cv::Scalar(255,0,255));
}
cv::imshow("detection", frame);
@endcode
*/
CV_EXPORTS bool getFaces(InputArray image, OutputArray faces, CParams* params);
CV_EXPORTS_W bool getFacesHAAR(InputArray image, OutputArray faces, const String& face_cascade_name);
/** @brief A utility to load list of paths to training image and annotation file.
@param imageList The specified file contains paths to the training images.
@param annotationList The specified file contains paths to the training annotations.
@param images The loaded paths of training images.
@param annotations The loaded paths of annotation files.
Example of usage:
@code
String imageFiles = "images_path.txt";
String ptsFiles = "annotations_path.txt";
std::vector<String> images_train;
std::vector<String> landmarks_train;
loadDatasetList(imageFiles,ptsFiles,images_train,landmarks_train);
@endcode
*/
CV_EXPORTS_W bool loadDatasetList(String imageList,
String annotationList,
std::vector<String> & images,
std::vector<String> & annotations);
/** @brief A utility to load facial landmark dataset from a single file.
@param filename The filename of a file that contains the dataset information.
Each line contains the filename of an image followed by
pairs of x and y values of facial landmarks points separated by a space.
Example
@code
/home/user/ibug/image_003_1.jpg 336.820955 240.864510 334.238298 260.922709 335.266918 ...
/home/user/ibug/image_005_1.jpg 376.158428 230.845712 376.736984 254.924635 383.265403 ...
@endcode
@param images A vector where each element represent the filename of image in the dataset.
Images are not loaded by default to save the memory.
@param facePoints The loaded landmark points for all training data.
@param delim Delimiter between each element, the default value is a whitespace.
@param offset An offset value to adjust the loaded points.
<B>Example of usage</B>
@code
cv::String imageFiles = "../data/images_train.txt";
cv::String ptsFiles = "../data/points_train.txt";
std::vector<String> images;
std::vector<std::vector<Point2f> > facePoints;
loadTrainingData(imageFiles, ptsFiles, images, facePoints, 0.0f);
@endcode
*/
CV_EXPORTS_W bool loadTrainingData( String filename , std::vector<String> & images,
OutputArray facePoints,
char delim = ' ', float offset = 0.0f);
/** @brief A utility to load facial landmark information from the dataset.
@param imageList A file contains the list of image filenames in the training dataset.
@param groundTruth A file contains the list of filenames
where the landmarks points information are stored.
The content in each file should follow the standard format (see face::loadFacePoints).
@param images A vector where each element represent the filename of image in the dataset.
Images are not loaded by default to save the memory.
@param facePoints The loaded landmark points for all training data.
@param offset An offset value to adjust the loaded points.
<B>Example of usage</B>
@code
cv::String imageFiles = "../data/images_train.txt";
cv::String ptsFiles = "../data/points_train.txt";
std::vector<String> images;
std::vector<std::vector<Point2f> > facePoints;
loadTrainingData(imageFiles, ptsFiles, images, facePoints, 0.0f);
@endcode
example of content in the images_train.txt
@code
/home/user/ibug/image_003_1.jpg
/home/user/ibug/image_004_1.jpg
/home/user/ibug/image_005_1.jpg
/home/user/ibug/image_006.jpg
@endcode
example of content in the points_train.txt
@code
/home/user/ibug/image_003_1.pts
/home/user/ibug/image_004_1.pts
/home/user/ibug/image_005_1.pts
/home/user/ibug/image_006.pts
@endcode
*/
CV_EXPORTS_W bool loadTrainingData( String imageList, String groundTruth,
std::vector<String> & images,
OutputArray facePoints,
float offset = 0.0f);
/** @brief This function extracts the data for training from .txt files which contains the corresponding image name and landmarks.
*The first file in each file should give the path of the image whose
*landmarks are being described in the file. Then in the subsequent
*lines there should be coordinates of the landmarks in the image
*i.e each line should be of the form x,y
*where x represents the x coordinate of the landmark and y represents
*the y coordinate of the landmark.
*
*For reference you can see the files as provided in the
*<a href="http://www.ifp.illinois.edu/~vuongle2/helen/">HELEN dataset</a>
*
* @param filename A vector of type cv::String containing name of the .txt files.
* @param trainlandmarks A vector of type cv::Point2f that would store shape or landmarks of all images.
* @param trainimages A vector of type cv::String which stores the name of images whose landmarks are tracked
* @returns A boolean value. It returns true when it reads the data successfully and false otherwise
*/
CV_EXPORTS_W bool loadTrainingData(std::vector<String> filename,std::vector< std::vector<Point2f> >
&trainlandmarks,std::vector<String> & trainimages);
/** @brief A utility to load facial landmark information from a given file.
@param filename The filename of file contains the facial landmarks data.
@param points The loaded facial landmark points.
@param offset An offset value to adjust the loaded points.
<B>Example of usage</B>
@code
std::vector<Point2f> points;
face::loadFacePoints("filename.txt", points, 0.0f);
@endcode
The annotation file should follow the default format which is
@code
version: 1
n_points: 68
{
212.716603 499.771793
230.232816 566.290071
...
}
@endcode
where n_points is the number of points considered
and each point is represented as its position in x and y.
*/
CV_EXPORTS_W bool loadFacePoints( String filename, OutputArray points,
float offset = 0.0f);
/** @brief Utility to draw the detected facial landmark points
@param image The input image to be processed.
@param points Contains the data of points which will be drawn.
@param color The color of points in BGR format represented by cv::Scalar.
<B>Example of usage</B>
@code
std::vector<Rect> faces;
std::vector<std::vector<Point2f> > landmarks;
facemark->getFaces(img, faces);
facemark->fit(img, faces, landmarks);
for(int j=0;j<rects.size();j++){
face::drawFacemarks(frame, landmarks[j], Scalar(0,0,255));
}
@endcode
*/
CV_EXPORTS_W void drawFacemarks( InputOutputArray image, InputArray points,
Scalar color = Scalar(255,0,0));
/** @brief Abstract base class for trainable facemark models
To utilize this API in your program, please take a look at the @ref tutorial_table_of_content_facemark
### Description
The AAM and LBF facemark models in OpenCV are derived from the abstract base class FacemarkTrain, which
provides a unified access to those facemark algorithms in OpenCV.
Here is an example on how to declare facemark algorithm:
@code
// Using Facemark in your code:
Ptr<Facemark> facemark = FacemarkLBF::create();
@endcode
The typical pipeline for facemark detection is listed as follows:
- (Non-mandatory) Set a user defined face detection using FacemarkTrain::setFaceDetector.
The facemark algorithms are desgined to fit the facial points into a face.
Therefore, the face information should be provided to the facemark algorithm.
Some algorithms might provides a default face recognition function.
However, the users might prefer to use their own face detector to obtains the best possible detection result.
- (Non-mandatory) Training the model for a specific algorithm using FacemarkTrain::training.
In this case, the model should be automatically saved by the algorithm.
If the user already have a trained model, then this part can be omitted.
- Load the trained model using Facemark::loadModel.
- Perform the fitting via the Facemark::fit.
*/
class CV_EXPORTS_W FacemarkTrain : public Facemark
{
public:
/** @brief Add one training sample to the trainer.
@param image Input image.
@param landmarks The ground-truth of facial landmarks points corresponds to the image.
<B>Example of usage</B>
@code
String imageFiles = "../data/images_train.txt";
String ptsFiles = "../data/points_train.txt";
std::vector<String> images_train;
std::vector<String> landmarks_train;
// load the list of dataset: image paths and landmark file paths
loadDatasetList(imageFiles,ptsFiles,images_train,landmarks_train);
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);
}
@endcode
The contents in the training files should follows the standard format.
Here are examples for the contents in these files.
example of content in the images_train.txt
@code
/home/user/ibug/image_003_1.jpg
/home/user/ibug/image_004_1.jpg
/home/user/ibug/image_005_1.jpg
/home/user/ibug/image_006.jpg
@endcode
example of content in the points_train.txt
@code
/home/user/ibug/image_003_1.pts
/home/user/ibug/image_004_1.pts
/home/user/ibug/image_005_1.pts
/home/user/ibug/image_006.pts
@endcode
*/
virtual bool addTrainingSample(InputArray image, InputArray landmarks)=0;
/** @brief Trains a Facemark algorithm using the given dataset.
Before the training process, training samples should be added to the trainer
using face::addTrainingSample function.
@param parameters Optional extra parameters (algorithm dependent).
<B>Example of usage</B>
@code
FacemarkLBF::Params params;
params.model_filename = "ibug68.model"; // filename to save the trained model
Ptr<Facemark> facemark = FacemarkLBF::create(params);
// add training samples (see Facemark::addTrainingSample)
facemark->training();
@endcode
*/
virtual void training(void* parameters=0)=0;
/** @brief Set a user defined face detector for the Facemark algorithm.
@param detector The user defined face detector function
@param userData Detector parameters
<B>Example of usage</B>
@code
MyDetectorParameters detectorParameters(...);
facemark->setFaceDetector(myDetector, &detectorParameters);
@endcode
Example of a user defined face detector
@code
bool myDetector( InputArray image, OutputArray faces, void* userData)
{
MyDetectorParameters* params = (MyDetectorParameters*)userData;
// -------- do something --------
}
@endcode
TODO Lifetime of detector parameters is uncontrolled. Rework interface design to "Ptr<FaceDetector>".
*/
virtual bool setFaceDetector(FN_FaceDetector detector, void* userData = 0)=0;
/** @brief Detect faces from a given image using default or user defined face detector.
Some Algorithm might not provide a default face detector.
@param image Input image.
@param faces Output of the function which represent region of interest of the detected faces. Each face is stored in cv::Rect container.
<B>Example of usage</B>
@code
std::vector<cv::Rect> faces;
facemark->getFaces(img, faces);
for(int j=0;j<faces.size();j++){
cv::rectangle(img, faces[j], cv::Scalar(255,0,255));
}
@endcode
*/
virtual bool getFaces(InputArray image, OutputArray faces)=0;
/** @brief Get data from an algorithm
@param items The obtained data, algorithm dependent.
<B>Example of usage</B>
@code
Ptr<FacemarkAAM> facemark = FacemarkAAM::create();
facemark->loadModel("AAM.yml");
FacemarkAAM::Data data;
facemark->getData(&data);
std::vector<Point2f> s0 = data.s0;
cout<<s0<<endl;
@endcode
*/
virtual bool getData(void * items=0)=0; // FIXIT
}; /* Facemark*/
//! @}
} /* namespace face */
} /* namespace cv */
#endif //__OPENCV_FACELANDMARKTRAIN_HPP__
{
"const_ignore_list": [
],
"const_private_list" : [
],
"missing_consts" : {
},
"ManualFuncs" : {
},
"func_arg_fix" : {
"fit" : {
"landmarks" : {"ctype" : "vector_vector_Point2f"},
"faces" : {"ctype" : "vector_Rect"}
}
}
}
import org.opencv.core.*;
import org.opencv.face.*;
import org.opencv.imgcodecs.*;
import org.opencv.imgproc.*;
import org.opencv.objdetect.*;
import java.util.*;
public class Facemark {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) {
if (args.length < 3) {
System.out.println("use: java Facemark [image file] [cascade file] [model file]");
return;
}
// read the image
Mat img = Imgcodecs.imread(args[0]);
// setup face detection
CascadeClassifier cascade = new CascadeClassifier(args[1]);
MatOfRect faces = new MatOfRect();
// detect faces
cascade.detectMultiScale(img, faces);
// setup landmarks detector
Facemark fm = Face.createFacemarkKazemi();
fm.loadModel(args[2]);
// fit landmarks for each found face
ArrayList<MatOfPoint2f> landmarks = new ArrayList<MatOfPoint2f>();
fm.fit(img, faces, landmarks);
// draw them
for (int i=0; i<landmarks.size(); i++) {
MatOfPoint2f lm = landmarks.get(i);
for (int j=0; j<lm.rows(); j++) {
double [] dp = lm.get(j,0);
Point p = new Point(dp[0], dp[1]);
Imgproc.circle(img,p,2,new Scalar(222),1);
}
}
// save result
Imgcodecs.imwrite("landmarks.jpg",img);
}
}
...@@ -149,7 +149,7 @@ int main(int argc, char** argv ) ...@@ -149,7 +149,7 @@ int main(int argc, char** argv )
printf(" - face with eyes found %i ", (int)conf.size()); printf(" - face with eyes found %i ", (int)conf.size());
std::vector<std::vector<Point2f> > landmarks; std::vector<std::vector<Point2f> > landmarks;
double newtime = (double)getTickCount(); double newtime = (double)getTickCount();
facemark->fit(image, faces_eyes, landmarks, (void*)&conf); facemark->fitConfig(image, faces_eyes, landmarks, conf);
double fittime = ((getTickCount() - newtime)/getTickFrequency()); double fittime = ((getTickCount() - newtime)/getTickFrequency());
for(unsigned j=0;j<landmarks.size();j++){ for(unsigned j=0;j<landmarks.size();j++){
drawFacemarks(image, landmarks[j],Scalar(0,255,0)); drawFacemarks(image, landmarks[j],Scalar(0,255,0));
......
...@@ -63,7 +63,7 @@ int main(int argc, char** argv) ...@@ -63,7 +63,7 @@ int main(int argc, char** argv)
FacemarkLBF::Params params; FacemarkLBF::Params params;
params.model_filename = model_path; params.model_filename = model_path;
params.cascade_face = cascade_path; params.cascade_face = cascade_path;
Ptr<Facemark> facemark = FacemarkLBF::create(params); Ptr<FacemarkLBF> facemark = FacemarkLBF::create(params);
CascadeClassifier face_cascade; CascadeClassifier face_cascade;
face_cascade.load(params.cascade_face.c_str()); face_cascade.load(params.cascade_face.c_str());
......
...@@ -74,7 +74,7 @@ int main(int argc, char** argv ){ ...@@ -74,7 +74,7 @@ int main(int argc, char** argv ){
params.model_filename = model_path; params.model_filename = model_path;
params.cascade_face = cascade_path; params.cascade_face = cascade_path;
Ptr<Facemark> facemark = FacemarkLBF::create(params); Ptr<FacemarkLBF> facemark = FacemarkLBF::create(params);
facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade); facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
facemark->loadModel(params.model_filename.c_str()); facemark->loadModel(params.model_filename.c_str());
......
/*---------------------------------------------- /*----------------------------------------------
* the user should provides the list of training images_train * the user should provide the list of training images_train,
* accompanied by their corresponding landmarks location in separated files. * accompanied by their corresponding landmarks location in separated files.
* example of contents for images.txt: * example of contents for images.txt:
* ../trainset/image_0001.png * ../trainset/image_0001.png
...@@ -131,4 +131,4 @@ int main(int argc,char** argv){ ...@@ -131,4 +131,4 @@ int main(int argc,char** argv){
facemark->training(Trainimages,Trainlandmarks,configfile_name,scale,modelfile_name); facemark->training(Trainimages,Trainlandmarks,configfile_name,scale,modelfile_name);
cout<<"Training complete"<<endl; cout<<"Training complete"<<endl;
return 0; return 0;
} }
\ No newline at end of file
...@@ -186,5 +186,10 @@ bool FacemarkKazemiImpl::scaleData( vector< vector<Point2f> > & trainlandmarks, ...@@ -186,5 +186,10 @@ bool FacemarkKazemiImpl::scaleData( vector< vector<Point2f> > & trainlandmarks,
Ptr<FacemarkKazemi> FacemarkKazemi::create(const FacemarkKazemi::Params &parameters){ Ptr<FacemarkKazemi> FacemarkKazemi::create(const FacemarkKazemi::Params &parameters){
return Ptr<FacemarkKazemiImpl>(new FacemarkKazemiImpl(parameters)); return Ptr<FacemarkKazemiImpl>(new FacemarkKazemiImpl(parameters));
} }
Ptr<Facemark> createFacemarkKazemi() {
FacemarkKazemi::Params parameters;
return Ptr<FacemarkKazemiImpl>(new FacemarkKazemiImpl(parameters));
}
}//cv }//cv
}//face }//face
\ No newline at end of file
...@@ -73,11 +73,14 @@ public: ...@@ -73,11 +73,14 @@ public:
void loadModel(String fs) CV_OVERRIDE; void loadModel(String fs) CV_OVERRIDE;
bool setFaceDetector(FN_FaceDetector f, void* userdata) CV_OVERRIDE; bool setFaceDetector(FN_FaceDetector f, void* userdata) CV_OVERRIDE;
bool getFaces(InputArray image, OutputArray faces) CV_OVERRIDE; bool getFaces(InputArray image, OutputArray faces) CV_OVERRIDE;
bool fit(InputArray image, InputArray faces, InputOutputArray landmarks) CV_OVERRIDE; bool fit(InputArray image, InputArray faces, OutputArrayOfArrays landmarks ) CV_OVERRIDE;
void training(String imageList, String groundTruth) CV_OVERRIDE; void training(String imageList, String groundTruth);
bool training(vector<Mat>& images, vector< vector<Point2f> >& landmarks,string filename,Size scale,string modelFilename) CV_OVERRIDE; bool training(vector<Mat>& images, vector< vector<Point2f> >& landmarks,string filename,Size scale,string modelFilename) CV_OVERRIDE;
// Destructor for the class. // Destructor for the class.
virtual ~FacemarkKazemiImpl(); virtual ~FacemarkKazemiImpl() CV_OVERRIDE;
virtual void read( const FileNode& ) CV_OVERRIDE {}
virtual void write( FileStorage& ) const CV_OVERRIDE {}
protected: protected:
FacemarkKazemi::Params params; FacemarkKazemi::Params params;
......
...@@ -10,7 +10,7 @@ Mentor: Delia Passalacqua ...@@ -10,7 +10,7 @@ Mentor: Delia Passalacqua
*/ */
#include "precomp.hpp" #include "precomp.hpp"
#include "opencv2/face.hpp" #include "opencv2/face/facemark_train.hpp"
/*dataset parser*/ /*dataset parser*/
#include <fstream> #include <fstream>
......
...@@ -103,9 +103,12 @@ public: ...@@ -103,9 +103,12 @@ public:
bool getData(void * items) CV_OVERRIDE; bool getData(void * items) CV_OVERRIDE;
bool fitConfig( InputArray image, InputArray roi, OutputArrayOfArrays _landmarks, const std::vector<Config> &runtime_params );
protected: protected:
bool fit( InputArray image, InputArray faces, InputOutputArray landmarks, void * runtime_params) CV_OVERRIDE;//!< from many ROIs bool fit( InputArray image, InputArray faces, OutputArrayOfArrays landmarks );
//bool fit( InputArray image, InputArray faces, InputOutputArray landmarks, void * runtime_params);//!< from many ROIs
bool fitImpl( const Mat image, std::vector<Point2f>& landmarks,const Mat R,const Point2f T,const float scale, const int sclIdx=0 ); bool fitImpl( const Mat image, std::vector<Point2f>& landmarks,const Mat R,const Point2f T,const float scale, const int sclIdx=0 );
bool addTrainingSample(InputArray image, InputArray landmarks) CV_OVERRIDE; bool addTrainingSample(InputArray image, InputArray landmarks) CV_OVERRIDE;
...@@ -153,6 +156,14 @@ Ptr<FacemarkAAM> FacemarkAAM::create(const FacemarkAAM::Params &parameters){ ...@@ -153,6 +156,14 @@ Ptr<FacemarkAAM> FacemarkAAM::create(const FacemarkAAM::Params &parameters){
return Ptr<FacemarkAAMImpl>(new FacemarkAAMImpl(parameters)); return Ptr<FacemarkAAMImpl>(new FacemarkAAMImpl(parameters));
} }
/*
* Constructor
*/
Ptr<Facemark> createFacemarkAAM(){
FacemarkAAM::Params parameters;
return Ptr<FacemarkAAMImpl>(new FacemarkAAMImpl(parameters));
}
FacemarkAAMImpl::FacemarkAAMImpl( const FacemarkAAM::Params &parameters ) : FacemarkAAMImpl::FacemarkAAMImpl( const FacemarkAAM::Params &parameters ) :
params( parameters ), params( parameters ),
faceDetector(NULL), faceDetectorData(NULL) faceDetector(NULL), faceDetectorData(NULL)
...@@ -312,7 +323,13 @@ void FacemarkAAMImpl::training(void* parameters){ ...@@ -312,7 +323,13 @@ void FacemarkAAMImpl::training(void* parameters){
if(params.verbose) printf("Training is completed\n"); if(params.verbose) printf("Training is completed\n");
} }
bool FacemarkAAMImpl::fit( InputArray image, InputArray roi, InputOutputArray _landmarks, void * runtime_params) bool FacemarkAAMImpl::fit( InputArray image, InputArray roi, OutputArrayOfArrays _landmarks )
{
std::vector<Config> config; // empty
return fitConfig(image, roi, _landmarks, config);
}
bool FacemarkAAMImpl::fitConfig( InputArray image, InputArray roi, OutputArrayOfArrays _landmarks, const std::vector<Config> &configs )
{ {
std::vector<Rect> & faces = *(std::vector<Rect> *)roi.getObj(); std::vector<Rect> & faces = *(std::vector<Rect> *)roi.getObj();
if(faces.size()<1) return false; if(faces.size()<1) return false;
...@@ -322,14 +339,13 @@ bool FacemarkAAMImpl::fit( InputArray image, InputArray roi, InputOutputArray _l ...@@ -322,14 +339,13 @@ bool FacemarkAAMImpl::fit( InputArray image, InputArray roi, InputOutputArray _l
landmarks.resize(faces.size()); landmarks.resize(faces.size());
Mat img = image.getMat(); Mat img = image.getMat();
if(runtime_params!=0){ if (! configs.empty()){
std::vector<Config> conf = *(std::vector<Config>*)runtime_params; if (configs.size()!=faces.size()) {
if (conf.size()!=faces.size()) {
CV_Error(Error::StsBadArg, "Number of faces and extra_parameters are different!"); CV_Error(Error::StsBadArg, "Number of faces and extra_parameters are different!");
} }
for(size_t i=0; i<conf.size();i++){ for(size_t i=0; i<configs.size();i++){
fitImpl(img, landmarks[i], conf[i].R,conf[i].t, conf[i].scale, conf[i].model_scale_idx); fitImpl(img, landmarks[i], configs[i].R,configs[i].t, configs[i].scale, configs[i].model_scale_idx);
} }
}else{ }else{
Mat R = Mat::eye(2, 2, CV_32F); Mat R = Mat::eye(2, 2, CV_32F);
......
...@@ -115,7 +115,7 @@ public: ...@@ -115,7 +115,7 @@ public:
protected: protected:
bool fit( InputArray image, InputArray faces, InputOutputArray landmarks, void * runtime_params ) CV_OVERRIDE;//!< from many ROIs bool fit( InputArray image, InputArray faces, OutputArrayOfArrays landmarks );//!< from many ROIs
bool fitImpl( const Mat image, std::vector<Point2f> & landmarks );//!< from a face bool fitImpl( const Mat image, std::vector<Point2f> & landmarks );//!< from a face
bool addTrainingSample(InputArray image, InputArray landmarks) CV_OVERRIDE; bool addTrainingSample(InputArray image, InputArray landmarks) CV_OVERRIDE;
...@@ -248,6 +248,13 @@ private: ...@@ -248,6 +248,13 @@ private:
Ptr<FacemarkLBF> FacemarkLBF::create(const FacemarkLBF::Params &parameters){ Ptr<FacemarkLBF> FacemarkLBF::create(const FacemarkLBF::Params &parameters){
return Ptr<FacemarkLBFImpl>(new FacemarkLBFImpl(parameters)); return Ptr<FacemarkLBFImpl>(new FacemarkLBFImpl(parameters));
} }
/*
* Constructor
*/
Ptr<Facemark> createFacemarkLBF(){
const FacemarkLBF::Params parameters;
return Ptr<FacemarkLBFImpl>(new FacemarkLBFImpl(parameters));
}
FacemarkLBFImpl::FacemarkLBFImpl( const FacemarkLBF::Params &parameters ) : FacemarkLBFImpl::FacemarkLBFImpl( const FacemarkLBF::Params &parameters ) :
faceDetector(NULL), faceDetectorData(NULL) faceDetector(NULL), faceDetectorData(NULL)
...@@ -363,10 +370,8 @@ void FacemarkLBFImpl::training(void* parameters){ ...@@ -363,10 +370,8 @@ void FacemarkLBFImpl::training(void* parameters){
isModelTrained = true; isModelTrained = true;
} }
bool FacemarkLBFImpl::fit( InputArray image, InputArray roi, InputOutputArray _landmarks, void * runtime_params ) bool FacemarkLBFImpl::fit( InputArray image, InputArray roi, OutputArrayOfArrays _landmarks )
{ {
CV_UNUSED(runtime_params);
// FIXIT // FIXIT
std::vector<Rect> & faces = *(std::vector<Rect> *)roi.getObj(); std::vector<Rect> & faces = *(std::vector<Rect> *)roi.getObj();
if (faces.empty()) return false; if (faces.empty()) return false;
......
...@@ -163,7 +163,7 @@ void FacemarkKazemiImpl :: loadModel(String filename){ ...@@ -163,7 +163,7 @@ void FacemarkKazemiImpl :: loadModel(String filename){
f.close(); f.close();
isModelLoaded = true; isModelLoaded = true;
} }
bool FacemarkKazemiImpl::fit(InputArray img, InputArray roi, InputOutputArray landmarks){ bool FacemarkKazemiImpl::fit(InputArray img, InputArray roi, OutputArrayOfArrays landmarks){
if(!isModelLoaded){ if(!isModelLoaded){
String error_message = "No model loaded. Aborting...."; String error_message = "No model loaded. Aborting....";
CV_Error(Error::StsBadArg, error_message); CV_Error(Error::StsBadArg, error_message);
...@@ -241,4 +241,4 @@ bool FacemarkKazemiImpl::fit(InputArray img, InputArray roi, InputOutputArray la ...@@ -241,4 +241,4 @@ bool FacemarkKazemiImpl::fit(InputArray img, InputArray roi, InputOutputArray la
return true; return true;
} }
}//cv }//cv
}//face }//face
\ No newline at end of file
...@@ -64,7 +64,7 @@ static bool customDetector( InputArray image, OutputArray ROIs, CascadeClassifie ...@@ -64,7 +64,7 @@ static bool customDetector( InputArray image, OutputArray ROIs, CascadeClassifie
TEST(CV_Face_FacemarkAAM, can_create_default) { TEST(CV_Face_FacemarkAAM, can_create_default) {
FacemarkAAM::Params params; FacemarkAAM::Params params;
Ptr<Facemark> facemark; Ptr<FacemarkAAM> facemark;
EXPECT_NO_THROW(facemark = FacemarkAAM::create(params)); EXPECT_NO_THROW(facemark = FacemarkAAM::create(params));
EXPECT_FALSE(facemark.empty()); EXPECT_FALSE(facemark.empty());
} }
...@@ -75,7 +75,7 @@ TEST(CV_Face_FacemarkAAM, can_set_custom_detector) { ...@@ -75,7 +75,7 @@ TEST(CV_Face_FacemarkAAM, can_set_custom_detector) {
CascadeClassifier face_detector; CascadeClassifier face_detector;
EXPECT_TRUE(face_detector.load(cascade_filename)); EXPECT_TRUE(face_detector.load(cascade_filename));
Ptr<Facemark> facemark = FacemarkAAM::create(); Ptr<FacemarkAAM> facemark = FacemarkAAM::create();
EXPECT_TRUE(facemark->setFaceDetector((cv::face::FN_FaceDetector)customDetector, &face_detector)); EXPECT_TRUE(facemark->setFaceDetector((cv::face::FN_FaceDetector)customDetector, &face_detector));
} }
...@@ -104,7 +104,7 @@ TEST(CV_Face_FacemarkAAM, test_workflow) { ...@@ -104,7 +104,7 @@ TEST(CV_Face_FacemarkAAM, test_workflow) {
params.m = 1; params.m = 1;
params.verbose = false; params.verbose = false;
params.save_model = false; params.save_model = false;
Ptr<Facemark> facemark = FacemarkAAM::create(params); Ptr<FacemarkAAM> facemark = FacemarkAAM::create(params);
Mat image; Mat image;
std::vector<Point2f> landmarks; std::vector<Point2f> landmarks;
......
...@@ -70,7 +70,7 @@ TEST(CV_Face_FacemarkLBF, can_create_default) { ...@@ -70,7 +70,7 @@ TEST(CV_Face_FacemarkLBF, can_create_default) {
FacemarkLBF::Params params; FacemarkLBF::Params params;
params.n_landmarks = 68; params.n_landmarks = 68;
Ptr<Facemark> facemark; Ptr<FacemarkLBF> facemark;
EXPECT_NO_THROW(facemark = FacemarkLBF::create(params)); EXPECT_NO_THROW(facemark = FacemarkLBF::create(params));
EXPECT_FALSE(facemark.empty()); EXPECT_FALSE(facemark.empty());
} }
...@@ -81,7 +81,7 @@ TEST(CV_Face_FacemarkLBF, can_set_custom_detector) { ...@@ -81,7 +81,7 @@ TEST(CV_Face_FacemarkLBF, can_set_custom_detector) {
EXPECT_TRUE(cascade_detector.load(cascade_filename)); EXPECT_TRUE(cascade_detector.load(cascade_filename));
Ptr<Facemark> facemark = FacemarkLBF::create(); Ptr<FacemarkLBF> facemark = FacemarkLBF::create();
EXPECT_TRUE(facemark->setFaceDetector(myCustomDetector)); EXPECT_TRUE(facemark->setFaceDetector(myCustomDetector));
} }
...@@ -107,7 +107,7 @@ TEST(CV_Face_FacemarkLBF, test_workflow) { ...@@ -107,7 +107,7 @@ TEST(CV_Face_FacemarkLBF, test_workflow) {
params.verbose = false; params.verbose = false;
params.save_model = false; params.save_model = false;
Ptr<Facemark> facemark = FacemarkLBF::create(params); Ptr<FacemarkLBF> facemark = FacemarkLBF::create(params);
Mat image; Mat image;
std::vector<Point2f> landmarks; std::vector<Point2f> landmarks;
......
...@@ -26,72 +26,66 @@ This tutorial will explain the sample code for face landmark detection. Jumping ...@@ -26,72 +26,66 @@ This tutorial will explain the sample code for face landmark detection. Jumping
``` c++ ``` c++
CascadeClassifier face_cascade; CascadeClassifier face_cascade;
bool myDetector( InputArray image, OutputArray ROIs ); face_cascade.load(cascade_name);
bool myDetector( InputArray image, OutputArray ROIs ){ Mat img = imread(image);
Mat gray; Ptr<Facemark> facemark = createFacemarkKazemi());
std::vector<Rect> faces; facemark->loadModel(filename);
if(image.channels()>1){ cout<<"Loaded model"<<endl;
cvtColor(image.getMat(),gray,CV_BGR2GRAY);
}
else{
gray = image.getMat().clone();
}
equalizeHist( gray, gray );
face_cascade.detectMultiScale( gray, faces, 1.1, 3,0, Size(30, 30) );
Mat(faces).copyTo(ROIs);
return true;
}
``` ```
The facemark API provides the functionality to the user to use their own face detector to be used in training.The above code creartes a sample face detector. The above function would be passed to a function pointer in the facemark API. The above code creates a CascadeClassifier to detect face regions, and an instance of the face landmark detection class.
We need to load a pretrained model for face landmark detection, and a cascade file for the face detection.
It also loads the image in which landmarks have to be detected.
``` c++
Mat img = imread(image);
face_cascade.load(cascade_name);
FacemarkKazemi::Params params;
params.configfile = configfile_name;
Ptr<Facemark> facemark = FacemarkKazemi::create(params);
facemark->setFaceDetector(myDetector);
```
The above code creates a pointer of the face landmark detection class. The face detector created above has to be passed
as function pointer to the facemark pointer created for detecting faces while landmark detection. The above code also loads the image
in which landmarks have to be detected.
``` c++ ``` c++
facemark->loadModel(filename);
cout<<"Loaded model"<<endl;
vector<Rect> faces; vector<Rect> faces;
resize(img,img,Size(460,460),0,0,INTER_LINEAR_EXACT); resize(img,img,Size(460,460),0,0,INTER_LINEAR_EXACT);
facemark->getFaces(img,faces);
vector< vector<Point2f> > shapes;
``` c++ Mat gray;
The above code loads a trained model for face landmark detection and creates a vector to store the detected faces. It then resizes the image to a smaller size as processing speed is faster with small images. It then creates a vector of vector to store shapes for each face detected. std::vector<Rect> faces;
if(img.channels()>1){
cvtColor(img.getMat(),gray,CV_BGR2GRAY);
}
else{
gray = img.getMat().clone();
}
equalizeHist( gray, gray );
face_cascade.detectMultiScale( gray, faces, 1.1, 3,0, Size(30, 30) );
```
After doing some preprocessing, we first have to detect possible face regions (which will be stored in a `vector<Rect>`.
Also, the image is resized to a smaller size as processing speed is faster with small images.
``` c++ ``` c++
if(facemark->fit(img,faces,shapes)) vector< vector<Point2f> > shapes;
{
for( size_t i = 0; i < faces.size(); i++ ) if (facemark->fit(img,faces,shapes))
{ {
cv::rectangle(img,faces[i],Scalar( 255, 0, 0 )); for ( size_t i = 0; i < faces.size(); i++ )
} {
for(unsigned long i=0;i<faces.size();i++){ cv::rectangle(img,faces[i],Scalar( 255, 0, 0 ));
for(unsigned long k=0;k<shapes[i].size();k++) }
cv::circle(img,shapes[i][k],5,cv::Scalar(0,0,255),FILLED); for (unsigned long i=0;i<faces.size();i++){
} for(unsigned long k=0;k<shapes[i].size();k++)
namedWindow("Detected_shape"); cv::circle(img,shapes[i][k],5,cv::Scalar(0,0,255),FILLED);
imshow("Detected_shape",img); }
waitKey(0); namedWindow("Detected_shape");
imshow("Detected_shape",img);
waitKey(0);
} }
``` ```
The above code then calls the function fit to get shapes of all detected faces in the image
It then creates a vector of vector to store shapes for each face detected.
The above code calls the function fit to get shapes of all detected faces in the image
and then draws the rectangles bounding the faces and marks the desired landmarks. and then draws the rectangles bounding the faces and marks the desired landmarks.
### Detection Results ### Detection Results
![](ab.jpg) ![](ab.jpg)
![](ab-1.jpg) ![](ab-1.jpg)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment