Commit 1864cc4c authored by Vadim Pisarevsky's avatar Vadim Pisarevsky

Merge pull request #1239 from sovrasov:face_refactoring

parents b593cae0 087210de
......@@ -318,7 +318,7 @@ public:
The suffix const means that prediction does not affect the internal model state, so the method can
be safely called from within different threads.
*/
CV_WRAP virtual void save(const String& filename) const;
CV_WRAP virtual void write(const String& filename) const;
/** @brief Loads a FaceRecognizer and its model state.
......@@ -327,16 +327,16 @@ public:
FaceRecognizer::load(FileStorage& fs) in turn gets called by
FaceRecognizer::load(const String& filename), to ease saving a model.
*/
CV_WRAP virtual void load(const String& filename);
CV_WRAP virtual void read(const String& filename);
/** @overload
Saves this model to a given FileStorage.
@param fs The FileStorage to store this FaceRecognizer to.
*/
virtual void save(FileStorage& fs) const = 0;
virtual void write(FileStorage& fs) const = 0;
/** @overload */
virtual void load(const FileStorage& fs) = 0;
virtual void read(const FileNode& fn) = 0;
/** @brief Sets string info for the specified model's label.
......
......@@ -68,14 +68,14 @@ public:
*/
CV_WRAP virtual void compute(InputArray image,
OutputArray features) const = 0;
};
/**
* @param num_bands The number of filter bands (<=8) used for computing BIF.
* @param num_rotations The number of image rotations for computing BIF.
* @returns Object for computing BIF.
*/
CV_EXPORTS_W cv::Ptr<BIF> createBIF(int num_bands = 8, int num_rotations = 12);
/**
* @param num_bands The number of filter bands (<=8) used for computing BIF.
* @param num_rotations The number of image rotations for computing BIF.
* @returns Object for computing BIF.
*/
CV_WRAP static Ptr<BIF> create(int num_bands = 8, int num_rotations = 12);
};
} // namespace cv
} // namespace face
......
......@@ -121,14 +121,14 @@ int main(int argc, const char *argv[]) {
// 10 principal components (read Eigenfaces), then call
// the factory method like this:
//
// cv::createEigenFaceRecognizer(10);
// EigenFaceRecognizer::create(10);
//
// If you want to create a FaceRecognizer with a
// confidennce threshold, call it with:
//
// cv::createEigenFaceRecognizer(10, 123.0);
// EigenFaceRecognizer::create(10, 123.0);
//
Ptr<BasicFaceRecognizer> model = createEigenFaceRecognizer();
Ptr<EigenFaceRecognizer> model = EigenFaceRecognizer::create();
for( int i = 0; i < nlabels; i++ )
model->setLabelInfo(i, labelsInfo[i]);
model->train(images, labels);
......
......@@ -115,19 +115,19 @@ int main(int argc, const char *argv[]) {
// 10 principal components (read Eigenfaces), then call
// the factory method like this:
//
// cv::createEigenFaceRecognizer(10);
// EigenFaceRecognizer::create(10);
//
// If you want to create a FaceRecognizer with a
// confidence threshold (e.g. 123.0), call it with:
//
// cv::createEigenFaceRecognizer(10, 123.0);
// EigenFaceRecognizer::create(10, 123.0);
//
// If you want to use _all_ Eigenfaces and have a threshold,
// then call the method like this:
//
// cv::createEigenFaceRecognizer(0, 123.0);
// EigenFaceRecognizer::create(0, 123.0);
//
Ptr<BasicFaceRecognizer> model = createEigenFaceRecognizer();
Ptr<EigenFaceRecognizer> model = EigenFaceRecognizer::create();
model->train(images, labels);
// The following line predicts the label of a given
// test image:
......
......@@ -114,7 +114,7 @@ int main(int argc, const char *argv[]) {
// If you just want to keep 10 Fisherfaces, then call
// the factory method like this:
//
// cv::createFisherFaceRecognizer(10);
// FisherFaceRecognizer::create(10);
//
// However it is not useful to discard Fisherfaces! Please
// always try to use _all_ available Fisherfaces for
......@@ -124,9 +124,9 @@ int main(int argc, const char *argv[]) {
// confidence threshold (e.g. 123.0) and use _all_
// Fisherfaces, then call it with:
//
// cv::createFisherFaceRecognizer(0, 123.0);
// FisherFaceRecognizer::create(0, 123.0);
//
Ptr<BasicFaceRecognizer> model = createFisherFaceRecognizer();
Ptr<FisherFaceRecognizer> model = FisherFaceRecognizer::create();
model->train(images, labels);
// The following line predicts the label of a given
// test image:
......
......@@ -103,7 +103,7 @@ int main(int argc, const char *argv[]) {
//
// cv::createLBPHFaceRecognizer(1,8,8,8,123.0)
//
Ptr<LBPHFaceRecognizer> model = createLBPHFaceRecognizer();
Ptr<LBPHFaceRecognizer> model = LBPHFaceRecognizer::create();
model->train(images, labels);
// The following line predicts the label of a given
// test image:
......
......@@ -127,7 +127,7 @@ int main(int argc, const char *argv[]) {
//
// cv::createEigenFaceRecognizer(0, 123.0);
//
Ptr<BasicFaceRecognizer> model0 = createEigenFaceRecognizer();
Ptr<EigenFaceRecognizer> model0 = EigenFaceRecognizer::create();
model0->train(images, labels);
// save the model to eigenfaces_at.yaml
model0->save("eigenfaces_at.yml");
......@@ -135,8 +135,7 @@ int main(int argc, const char *argv[]) {
//
// Now create a new Eigenfaces Recognizer
//
Ptr<BasicFaceRecognizer> model1 = createEigenFaceRecognizer();
model1->load("eigenfaces_at.yml");
Ptr<EigenFaceRecognizer> model1 = Algorithm::load<EigenFaceRecognizer>("eigenfaces_at.yml");
// The following line predicts the label of a given
// test image:
int predictedLabel = model1->predict(testSample);
......
......@@ -79,7 +79,7 @@ int main(int argc, const char *argv[]) {
int im_width = images[0].cols;
int im_height = images[0].rows;
// Create a FaceRecognizer and train it on the given images:
Ptr<BasicFaceRecognizer> model = createFisherFaceRecognizer();
Ptr<FisherFaceRecognizer> model = FisherFaceRecognizer::create();
model->train(images, labels);
// That's it for learning the Face Recognition model. You now
// need to create the classifier for the task of Face Detection.
......
......@@ -216,6 +216,6 @@ void BIFImpl::computeUnit(int unit_idx, const cv::Mat &img,
} // namespace
cv::Ptr<cv::face::BIF> cv::face::createBIF(int num_bands, int num_rotations) {
cv::Ptr<cv::face::BIF> cv::face::BIF::create(int num_bands, int num_rotations) {
return cv::Ptr<cv::face::BIF>(new BIFImpl(num_bands, num_rotations));
}
......@@ -16,7 +16,8 @@
* See <http://www.opensource.org/licenses/bsd-license>
*/
#include "precomp.hpp"
#include "face_basic.hpp"
#include <opencv2/face.hpp>
#include "face_utils.hpp"
#include <set>
#include <limits>
#include <iostream>
......@@ -28,14 +29,17 @@ namespace face
// Turk, M., and Pentland, A. "Eigenfaces for recognition.". Journal of
// Cognitive Neuroscience 3 (1991), 71–86.
class Eigenfaces : public BasicFaceRecognizerImpl
class Eigenfaces : public EigenFaceRecognizer
{
public:
// Initializes an empty Eigenfaces model.
Eigenfaces(int num_components = 0, double threshold = DBL_MAX)
: BasicFaceRecognizerImpl(num_components, threshold)
{}
//: BasicFaceRecognizerImpl(num_components, threshold)
{
_num_components = num_components;
_threshold = threshold;
}
// Computes an Eigenfaces model with images in src and corresponding labels
// in labels.
......@@ -122,7 +126,7 @@ void Eigenfaces::predict(InputArray _src, Ptr<PredictCollector> collector) const
}
}
Ptr<BasicFaceRecognizer> createEigenFaceRecognizer(int num_components, double threshold)
Ptr<EigenFaceRecognizer> EigenFaceRecognizer::create(int num_components, double threshold)
{
return makePtr<Eigenfaces>(num_components, threshold);
}
......
#include "opencv2/face.hpp"
#include "face_utils.hpp"
#include "precomp.hpp"
using namespace cv;
using namespace face;
int BasicFaceRecognizer::getNumComponents() const
{
return _num_components;
}
void BasicFaceRecognizer::setNumComponents(int val)
{
_num_components = val;
}
double BasicFaceRecognizer::getThreshold() const
{
return _threshold;
}
void BasicFaceRecognizer::setThreshold(double val)
{
_threshold = val;
}
std::vector<cv::Mat> BasicFaceRecognizer::getProjections() const
{
return _projections;
}
cv::Mat BasicFaceRecognizer::getLabels() const
{
return _labels;
}
cv::Mat BasicFaceRecognizer::getEigenValues() const
{
return _eigenvalues;
}
cv::Mat BasicFaceRecognizer::getEigenVectors() const
{
return _eigenvectors;
}
cv::Mat BasicFaceRecognizer::getMean() const
{
return _mean;
}
void BasicFaceRecognizer::read(const FileNode& fs)
{
//read matrices
fs["num_components"] >> _num_components;
fs["mean"] >> _mean;
fs["eigenvalues"] >> _eigenvalues;
fs["eigenvectors"] >> _eigenvectors;
// read sequences
readFileNodeList(fs["projections"], _projections);
fs["labels"] >> _labels;
const FileNode& fn = fs["labelsInfo"];
if (fn.type() == FileNode::SEQ)
{
_labelsInfo.clear();
for (FileNodeIterator it = fn.begin(); it != fn.end();)
{
LabelInfo item;
it >> item;
_labelsInfo.insert(std::make_pair(item.label, item.value));
}
}
}
void BasicFaceRecognizer::write(FileStorage& fs) const
{
// write matrices
fs << "num_components" << _num_components;
fs << "mean" << _mean;
fs << "eigenvalues" << _eigenvalues;
fs << "eigenvectors" << _eigenvectors;
// write sequences
writeFileNodeList(fs, "projections", _projections);
fs << "labels" << _labels;
fs << "labelsInfo" << "[";
for (std::map<int, String>::const_iterator it = _labelsInfo.begin(); it != _labelsInfo.end(); it++)
fs << LabelInfo(it->first, it->second);
fs << "]";
}
#ifndef __OPENCV_FACE_BASIC_HPP
#define __OPENCV_FACE_BASIC_HPP
// 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.
#include "opencv2/face.hpp"
#include "precomp.hpp"
#ifndef __OPENCV_FACE_UTILS_HPP
#define __OPENCV_FACE_UTILS_HPP
#include <set>
#include <limits>
#include <iostream>
#include "precomp.hpp"
using namespace cv;
// Reads a sequence from a FileNode::SEQ with type _Tp into a result vector.
template<typename _Tp>
inline void readFileNodeList(const FileNode& fn, std::vector<_Tp>& result) {
if (fn.type() == FileNode::SEQ) {
for (FileNodeIterator it = fn.begin(); it != fn.end();) {
_Tp item;
it >> item;
result.push_back(item);
}
}
}
// Writes the a list of given items to a cv::FileStorage.
template<typename _Tp>
inline void writeFileNodeList(FileStorage& fs, const String& name,
const std::vector<_Tp>& items) {
// typedefs
typedef typename std::vector<_Tp>::const_iterator constVecIterator;
// write the elements in item to fs
fs << name << "[";
for (constVecIterator it = items.begin(); it != items.end(); ++it) {
fs << *it;
}
fs << "]";
}
inline Mat asRowMatrix(InputArrayOfArrays src, int rtype, double alpha=1, double beta=0) {
// make sure the input data is a vector of matrices or vector of vector
if(src.kind() != _InputArray::STD_VECTOR_MAT && src.kind() != _InputArray::STD_VECTOR_VECTOR) {
......@@ -70,6 +43,32 @@ inline Mat asRowMatrix(InputArrayOfArrays src, int rtype, double alpha=1, double
return data;
}
// Reads a sequence from a FileNode::SEQ with type _Tp into a result vector.
template<typename _Tp>
inline void readFileNodeList(const FileNode& fn, std::vector<_Tp>& result) {
if (fn.type() == FileNode::SEQ) {
for (FileNodeIterator it = fn.begin(); it != fn.end();) {
_Tp item;
it >> item;
result.push_back(item);
}
}
}
// Writes the a list of given items to a cv::FileStorage.
template<typename _Tp>
inline void writeFileNodeList(FileStorage& fs, const String& name,
const std::vector<_Tp>& items) {
// typedefs
typedef typename std::vector<_Tp>::const_iterator constVecIterator;
// write the elements in item to fs
fs << name << "[";
for (constVecIterator it = items.begin(); it != items.end(); ++it) {
fs << *it;
}
fs << "]";
}
// Utility structure to load/save face label info (a pair of int and string) via FileStorage
struct LabelInfo
{
......@@ -106,70 +105,4 @@ inline void read(const cv::FileNode& node, LabelInfo& x, const LabelInfo& defaul
x.read(node);
}
class BasicFaceRecognizerImpl : public cv::face::BasicFaceRecognizer
{
public:
BasicFaceRecognizerImpl(int num_components = 0, double threshold = DBL_MAX)
: _num_components(num_components), _threshold(threshold)
{}
void load(const FileStorage& fs)
{
//read matrices
fs["num_components"] >> _num_components;
fs["mean"] >> _mean;
fs["eigenvalues"] >> _eigenvalues;
fs["eigenvectors"] >> _eigenvectors;
// read sequences
readFileNodeList(fs["projections"], _projections);
fs["labels"] >> _labels;
const FileNode& fn = fs["labelsInfo"];
if (fn.type() == FileNode::SEQ)
{
_labelsInfo.clear();
for (FileNodeIterator it = fn.begin(); it != fn.end();)
{
LabelInfo item;
it >> item;
_labelsInfo.insert(std::make_pair(item.label, item.value));
}
}
}
void save(FileStorage& fs) const
{
// write matrices
fs << "num_components" << _num_components;
fs << "mean" << _mean;
fs << "eigenvalues" << _eigenvalues;
fs << "eigenvectors" << _eigenvectors;
// write sequences
writeFileNodeList(fs, "projections", _projections);
fs << "labels" << _labels;
fs << "labelsInfo" << "[";
for (std::map<int, String>::const_iterator it = _labelsInfo.begin(); it != _labelsInfo.end(); it++)
fs << LabelInfo(it->first, it->second);
fs << "]";
}
CV_IMPL_PROPERTY(int, NumComponents, _num_components)
CV_IMPL_PROPERTY(double, Threshold, _threshold)
CV_IMPL_PROPERTY_RO(std::vector<cv::Mat>, Projections, _projections)
CV_IMPL_PROPERTY_RO(cv::Mat, Labels, _labels)
CV_IMPL_PROPERTY_RO(cv::Mat, EigenValues, _eigenvalues)
CV_IMPL_PROPERTY_RO(cv::Mat, EigenVectors, _eigenvectors)
CV_IMPL_PROPERTY_RO(cv::Mat, Mean, _mean)
protected:
int _num_components;
double _threshold;
std::vector<Mat> _projections;
Mat _labels;
Mat _eigenvectors;
Mat _eigenvalues;
Mat _mean;
};
#endif // __OPENCV_FACE_BASIC_HPP
#endif
......@@ -54,21 +54,21 @@ void FaceRecognizer::update(InputArrayOfArrays src, InputArray labels)
CV_Error(Error::StsNotImplemented, error_msg);
}
void FaceRecognizer::load(const String &filename)
void FaceRecognizer::read(const String &filename)
{
FileStorage fs(filename, FileStorage::READ);
if (!fs.isOpened())
CV_Error(Error::StsError, "File can't be opened for reading!");
this->load(fs);
this->read(fs.root());
fs.release();
}
void FaceRecognizer::save(const String &filename) const
void FaceRecognizer::write(const String &filename) const
{
FileStorage fs(filename, FileStorage::WRITE);
if (!fs.isOpened())
CV_Error(Error::StsError, "File can't be opened for writing!");
this->save(fs);
this->write(fs);
fs.release();
}
......
......@@ -16,7 +16,8 @@
* See <http://www.opensource.org/licenses/bsd-license>
*/
#include "precomp.hpp"
#include "face_basic.hpp"
#include <opencv2/face.hpp>
#include "face_utils.hpp"
namespace cv { namespace face {
......@@ -24,13 +25,16 @@ namespace cv { namespace face {
// faces: Recognition using class specific linear projection.". IEEE
// Transactions on Pattern Analysis and Machine Intelligence 19, 7 (1997),
// 711–720.
class Fisherfaces: public BasicFaceRecognizerImpl
class Fisherfaces: public FisherFaceRecognizer
{
public:
// Initializes an empty Fisherfaces model.
Fisherfaces(int num_components = 0, double threshold = DBL_MAX)
: BasicFaceRecognizerImpl(num_components, threshold)
{ }
//: BasicFaceRecognizer(num_components, threshold)
{
_num_components = num_components;
_threshold = threshold;
}
// Computes a Fisherfaces model with images in src and corresponding labels
// in labels.
......@@ -142,7 +146,7 @@ void Fisherfaces::predict(InputArray _src, Ptr<PredictCollector> collector) cons
}
}
Ptr<BasicFaceRecognizer> createFisherFaceRecognizer(int num_components, double threshold)
Ptr<FisherFaceRecognizer> FisherFaceRecognizer::create(int num_components, double threshold)
{
return makePtr<Fisherfaces>(num_components, threshold);
}
......
......@@ -17,7 +17,7 @@
*/
#include "precomp.hpp"
#include "opencv2/face.hpp"
#include "face_basic.hpp"
#include "face_utils.hpp"
namespace cv { namespace face {
......@@ -46,8 +46,8 @@ private:
public:
using FaceRecognizer::save;
using FaceRecognizer::load;
using FaceRecognizer::read;
using FaceRecognizer::write;
// Initializes this LBPH Model. The current implementation is rather fixed
// as it uses the Extended Local Binary Patterns per default.
......@@ -94,11 +94,11 @@ public:
// Send all predict results to caller side for custom result handling
void predict(InputArray src, Ptr<PredictCollector> collector) const;
// See FaceRecognizer::load.
void load(const FileStorage& fs);
// See FaceRecognizer::write.
void read(const FileNode& fn);
// See FaceRecognizer::save.
void save(FileStorage& fs) const;
void write(FileStorage& fs) const;
CV_IMPL_PROPERTY(int, GridX, _grid_x)
CV_IMPL_PROPERTY(int, GridY, _grid_y)
......@@ -110,7 +110,7 @@ public:
};
void LBPH::load(const FileStorage& fs) {
void LBPH::read(const FileNode& fs) {
fs["radius"] >> _radius;
fs["neighbors"] >> _neighbors;
fs["grid_x"] >> _grid_x;
......@@ -132,7 +132,7 @@ void LBPH::load(const FileStorage& fs) {
}
// See FaceRecognizer::save.
void LBPH::save(FileStorage& fs) const {
void LBPH::write(FileStorage& fs) const {
fs << "radius" << _radius;
fs << "neighbors" << _neighbors;
fs << "grid_x" << _grid_x;
......@@ -407,11 +407,10 @@ void LBPH::predict(InputArray _src, Ptr<PredictCollector> collector) const {
}
}
Ptr<LBPHFaceRecognizer> createLBPHFaceRecognizer(int radius, int neighbors,
Ptr<LBPHFaceRecognizer> LBPHFaceRecognizer::create(int radius, int neighbors,
int grid_x, int grid_y, double threshold)
{
return makePtr<LBPH>(radius, neighbors, grid_x, grid_y, threshold);
}
}}
......@@ -49,10 +49,8 @@
#include "opencv2/core/private.hpp"
#include "opencv2/core/persistence.hpp"
#include <map>
#include <iostream>
#include <set>
#include <limits>
#include <iostream>
#endif
......@@ -40,27 +40,27 @@ the use of this software, even if advised of the possibility of such damage.
TEST(CV_Face_BIF, can_create_default) {
cv::Ptr<cv::face::BIF> bif;
EXPECT_NO_THROW(bif = cv::face::createBIF());
EXPECT_NO_THROW(bif = cv::face::BIF::create());
EXPECT_FALSE(bif.empty());
}
TEST(CV_Face_BIF, fails_when_zero_bands) {
EXPECT_ANY_THROW(cv::face::createBIF(0));
EXPECT_ANY_THROW(cv::face::BIF::create(0));
}
TEST(CV_Face_BIF, fails_when_too_many_bands) {
EXPECT_ANY_THROW(cv::face::createBIF(9));
EXPECT_ANY_THROW(cv::face::BIF::create(9));
}
TEST(CV_Face_BIF, fails_when_zero_rotations) {
EXPECT_ANY_THROW(cv::face::createBIF(8, 0));
EXPECT_ANY_THROW(cv::face::BIF::create(8, 0));
}
TEST(CV_Face_BIF, can_compute) {
cv::Mat image(60, 60, CV_32F);
cv::theRNG().fill(image, cv::RNG::UNIFORM, -1, 1);
cv::Ptr<cv::face::BIF> bif = cv::face::createBIF();
cv::Ptr<cv::face::BIF> bif = cv::face::BIF::create();
cv::Mat fea;
EXPECT_NO_THROW(bif->compute(image, fea));
EXPECT_EQ(cv::Size(1, 13188), fea.size());
......
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