Exceptions now go into CV_Error. Added thresholding to the FaceRecognizer and updated the demo accordingly.
This commit is contained in:
parent
ee1b671279
commit
cd7d93f362
@ -930,6 +930,9 @@ namespace cv
|
|||||||
// Gets a prediction from a FaceRecognizer.
|
// Gets a prediction from a FaceRecognizer.
|
||||||
virtual int predict(InputArray src) const = 0;
|
virtual int predict(InputArray src) const = 0;
|
||||||
|
|
||||||
|
// Predicts the label and confidence for a given sample.
|
||||||
|
virtual void predict(InputArray src, int &label, double &dist) const = 0;
|
||||||
|
|
||||||
// Serializes this object to a given filename.
|
// Serializes this object to a given filename.
|
||||||
virtual void save(const string& filename) const;
|
virtual void save(const string& filename) const;
|
||||||
|
|
||||||
@ -944,10 +947,10 @@ namespace cv
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CV_EXPORTS Ptr<FaceRecognizer> createEigenFaceRecognizer(int num_components = 0);
|
CV_EXPORTS Ptr<FaceRecognizer> createEigenFaceRecognizer(int num_components = 0, double threshold = DBL_MAX);
|
||||||
CV_EXPORTS Ptr<FaceRecognizer> createFisherFaceRecognizer(int num_components = 0);
|
CV_EXPORTS Ptr<FaceRecognizer> createFisherFaceRecognizer(int num_components = 0, double threshold = DBL_MAX);
|
||||||
CV_EXPORTS Ptr<FaceRecognizer> createLBPHFaceRecognizer(int radius=1, int neighbors=8,
|
CV_EXPORTS Ptr<FaceRecognizer> createLBPHFaceRecognizer(int radius=1, int neighbors=8,
|
||||||
int grid_x=8, int grid_y=8);
|
int grid_x=8, int grid_y=8, double threshold = DBL_MAX);
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -105,6 +105,7 @@ class Eigenfaces : public FaceRecognizer
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int _num_components;
|
int _num_components;
|
||||||
|
double _threshold;
|
||||||
vector<Mat> _projections;
|
vector<Mat> _projections;
|
||||||
Mat _labels;
|
Mat _labels;
|
||||||
Mat _eigenvectors;
|
Mat _eigenvectors;
|
||||||
@ -116,15 +117,18 @@ public:
|
|||||||
using FaceRecognizer::load;
|
using FaceRecognizer::load;
|
||||||
|
|
||||||
// Initializes an empty Eigenfaces model.
|
// Initializes an empty Eigenfaces model.
|
||||||
Eigenfaces(int num_components = 0) :
|
Eigenfaces(int num_components = 0, double threshold = DBL_MAX) :
|
||||||
_num_components(num_components) { }
|
_num_components(num_components),
|
||||||
|
_threshold(threshold) {}
|
||||||
|
|
||||||
// Initializes and computes an Eigenfaces model with images in src and
|
// Initializes and computes an Eigenfaces model with images in src and
|
||||||
// corresponding labels in labels. num_components will be kept for
|
// corresponding labels in labels. num_components will be kept for
|
||||||
// classification.
|
// classification.
|
||||||
Eigenfaces(InputArray src, InputArray labels,
|
Eigenfaces(InputArray src, InputArray labels,
|
||||||
int num_components = 0) :
|
int num_components = 0,
|
||||||
_num_components(num_components) {
|
double threshold = DBL_MAX) :
|
||||||
|
_num_components(num_components),
|
||||||
|
_threshold(threshold) {
|
||||||
train(src, labels);
|
train(src, labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +139,9 @@ public:
|
|||||||
// Predicts the label of a query image in src.
|
// Predicts the label of a query image in src.
|
||||||
int predict(InputArray src) const;
|
int predict(InputArray src) const;
|
||||||
|
|
||||||
|
// Predicts the label and confidence for a given sample.
|
||||||
|
void predict(InputArray _src, int &label, double &dist) const;
|
||||||
|
|
||||||
// See FaceRecognizer::load.
|
// See FaceRecognizer::load.
|
||||||
void load(const FileStorage& fs);
|
void load(const FileStorage& fs);
|
||||||
|
|
||||||
@ -152,6 +159,7 @@ class Fisherfaces: public FaceRecognizer
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int _num_components;
|
int _num_components;
|
||||||
|
double _threshold;
|
||||||
Mat _eigenvectors;
|
Mat _eigenvectors;
|
||||||
Mat _eigenvalues;
|
Mat _eigenvalues;
|
||||||
Mat _mean;
|
Mat _mean;
|
||||||
@ -163,16 +171,19 @@ public:
|
|||||||
using FaceRecognizer::load;
|
using FaceRecognizer::load;
|
||||||
|
|
||||||
// Initializes an empty Fisherfaces model.
|
// Initializes an empty Fisherfaces model.
|
||||||
Fisherfaces(int num_components = 0) :
|
Fisherfaces(int num_components = 0, double threshold = DBL_MAX) :
|
||||||
_num_components(num_components) {}
|
_num_components(num_components),
|
||||||
|
_threshold(threshold) {}
|
||||||
|
|
||||||
// Initializes and computes a Fisherfaces model with images in src and
|
// Initializes and computes a Fisherfaces model with images in src and
|
||||||
// corresponding labels in labels. num_components will be kept for
|
// corresponding labels in labels. num_components will be kept for
|
||||||
// classification.
|
// classification.
|
||||||
Fisherfaces(InputArray src,
|
Fisherfaces(InputArray src,
|
||||||
InputArray labels,
|
InputArray labels,
|
||||||
int num_components = 0) :
|
int num_components = 0,
|
||||||
_num_components(num_components) {
|
double threshold = DBL_MAX) :
|
||||||
|
_num_components(num_components),
|
||||||
|
_threshold(threshold) {
|
||||||
train(src, labels);
|
train(src, labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,6 +196,9 @@ public:
|
|||||||
// Predicts the label of a query image in src.
|
// Predicts the label of a query image in src.
|
||||||
int predict(InputArray src) const;
|
int predict(InputArray src) const;
|
||||||
|
|
||||||
|
// Predicts the label and confidence for a given sample.
|
||||||
|
void predict(InputArray _src, int &label, double &dist) const;
|
||||||
|
|
||||||
// See FaceRecognizer::load.
|
// See FaceRecognizer::load.
|
||||||
virtual void load(const FileStorage& fs);
|
virtual void load(const FileStorage& fs);
|
||||||
|
|
||||||
@ -207,6 +221,7 @@ private:
|
|||||||
int _grid_y;
|
int _grid_y;
|
||||||
int _radius;
|
int _radius;
|
||||||
int _neighbors;
|
int _neighbors;
|
||||||
|
double _threshold;
|
||||||
|
|
||||||
vector<Mat> _histograms;
|
vector<Mat> _histograms;
|
||||||
Mat _labels;
|
Mat _labels;
|
||||||
@ -220,11 +235,12 @@ public:
|
|||||||
//
|
//
|
||||||
// radius, neighbors are used in the local binary patterns creation.
|
// radius, neighbors are used in the local binary patterns creation.
|
||||||
// grid_x, grid_y control the grid size of the spatial histograms.
|
// grid_x, grid_y control the grid size of the spatial histograms.
|
||||||
LBPH(int radius=1, int neighbors=8, int grid_x=8, int grid_y=8) :
|
LBPH(int radius=1, int neighbors=8, int grid_x=8, int grid_y=8, double threshold = DBL_MAX) :
|
||||||
_grid_x(grid_x),
|
_grid_x(grid_x),
|
||||||
_grid_y(grid_y),
|
_grid_y(grid_y),
|
||||||
_radius(radius),
|
_radius(radius),
|
||||||
_neighbors(neighbors) {}
|
_neighbors(neighbors),
|
||||||
|
_threshold(threshold) {}
|
||||||
|
|
||||||
// Initializes and computes this LBPH Model. The current implementation is
|
// Initializes and computes this LBPH Model. The current implementation is
|
||||||
// rather fixed as it uses the Extended Local Binary Patterns per default.
|
// rather fixed as it uses the Extended Local Binary Patterns per default.
|
||||||
@ -234,11 +250,13 @@ public:
|
|||||||
LBPH(InputArray src,
|
LBPH(InputArray src,
|
||||||
InputArray labels,
|
InputArray labels,
|
||||||
int radius=1, int neighbors=8,
|
int radius=1, int neighbors=8,
|
||||||
int grid_x=8, int grid_y=8) :
|
int grid_x=8, int grid_y=8,
|
||||||
|
double threshold = DBL_MAX) :
|
||||||
_grid_x(grid_x),
|
_grid_x(grid_x),
|
||||||
_grid_y(grid_y),
|
_grid_y(grid_y),
|
||||||
_radius(radius),
|
_radius(radius),
|
||||||
_neighbors(neighbors) {
|
_neighbors(neighbors),
|
||||||
|
_threshold(threshold) {
|
||||||
train(src, labels);
|
train(src, labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,6 +269,9 @@ public:
|
|||||||
// Predicts the label of a query image in src.
|
// Predicts the label of a query image in src.
|
||||||
int predict(InputArray src) const;
|
int predict(InputArray src) const;
|
||||||
|
|
||||||
|
// Predicts the label and confidence for a given sample.
|
||||||
|
void predict(InputArray _src, int &label, double &dist) const;
|
||||||
|
|
||||||
// See FaceRecognizer::load.
|
// See FaceRecognizer::load.
|
||||||
void load(const FileStorage& fs);
|
void load(const FileStorage& fs);
|
||||||
|
|
||||||
@ -293,10 +314,10 @@ void FaceRecognizer::load(const string& filename) {
|
|||||||
void Eigenfaces::train(InputArray _src, InputArray _local_labels) {
|
void Eigenfaces::train(InputArray _src, InputArray _local_labels) {
|
||||||
if(_src.total() == 0) {
|
if(_src.total() == 0) {
|
||||||
string error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
|
string error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
|
||||||
error(Exception(CV_StsUnsupportedFormat, error_message, "Eigenfaces::train", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
} else if(_local_labels.getMat().type() != CV_32SC1) {
|
} else if(_local_labels.getMat().type() != CV_32SC1) {
|
||||||
string error_message = format("Labels must be given as integer (CV_32SC1). Expected %d, but was %d.", CV_32SC1, _local_labels.type());
|
string error_message = format("Labels must be given as integer (CV_32SC1). Expected %d, but was %d.", CV_32SC1, _local_labels.type());
|
||||||
error(Exception(CV_StsUnsupportedFormat, error_message, "Eigenfaces::train", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// get labels
|
// get labels
|
||||||
Mat labels = _local_labels.getMat();
|
Mat labels = _local_labels.getMat();
|
||||||
@ -307,7 +328,7 @@ void Eigenfaces::train(InputArray _src, InputArray _local_labels) {
|
|||||||
// assert there are as much samples as labels
|
// assert there are as much samples as labels
|
||||||
if(static_cast<int>(labels.total()) != n) {
|
if(static_cast<int>(labels.total()) != n) {
|
||||||
string error_message = format("The number of samples (src) must equal the number of labels (labels)! len(src)=%d, len(labels)=%d.", n, labels.total());
|
string error_message = format("The number of samples (src) must equal the number of labels (labels)! len(src)=%d, len(labels)=%d.", n, labels.total());
|
||||||
error(Exception(CV_StsBadArg, error_message, "Eigenfaces::train", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// clip number of components to be valid
|
// clip number of components to be valid
|
||||||
if((_num_components <= 0) || (_num_components > n))
|
if((_num_components <= 0) || (_num_components > n))
|
||||||
@ -326,31 +347,37 @@ void Eigenfaces::train(InputArray _src, InputArray _local_labels) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Eigenfaces::predict(InputArray _src) const {
|
void Eigenfaces::predict(InputArray _src, int &minClass, double &minDist) const {
|
||||||
// get data
|
// get data
|
||||||
Mat src = _src.getMat();
|
Mat src = _src.getMat();
|
||||||
// make sure the user is passing correct data
|
// make sure the user is passing correct data
|
||||||
if(_projections.empty()) {
|
if(_projections.empty()) {
|
||||||
// throw error if no data (or simply return -1?)
|
// throw error if no data (or simply return -1?)
|
||||||
string error_message = "This Eigenfaces model is not computed yet. Did you call Eigenfaces::train?";
|
string error_message = "This Eigenfaces model is not computed yet. Did you call Eigenfaces::train?";
|
||||||
error(cv::Exception(CV_StsError, error_message, "Eigenfaces::predict", __FILE__, __LINE__));
|
CV_Error(CV_StsError, error_message);
|
||||||
} else if(_eigenvectors.rows != static_cast<int>(src.total())) {
|
} else if(_eigenvectors.rows != static_cast<int>(src.total())) {
|
||||||
// check data alignment just for clearer exception messages
|
// check data alignment just for clearer exception messages
|
||||||
string error_message = format("Wrong input image size. Reason: Training and Test images must be of equal size! Expected an image with %d elements, but got %d.", _eigenvectors.rows, src.total());
|
string error_message = format("Wrong input image size. Reason: Training and Test images must be of equal size! Expected an image with %d elements, but got %d.", _eigenvectors.rows, src.total());
|
||||||
error(cv::Exception(CV_StsError, error_message, "Eigenfaces::predict", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// project into PCA subspace
|
// project into PCA subspace
|
||||||
Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1));
|
Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1));
|
||||||
double minDist = DBL_MAX;
|
minDist = DBL_MAX;
|
||||||
int minClass = -1;
|
minClass = -1;
|
||||||
for(size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) {
|
for(size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) {
|
||||||
double dist = norm(_projections[sampleIdx], q, NORM_L2);
|
double dist = norm(_projections[sampleIdx], q, NORM_L2);
|
||||||
if(dist < minDist) {
|
if((dist < minDist) && (dist < _threshold)) {
|
||||||
minDist = dist;
|
minDist = dist;
|
||||||
minClass = _labels.at<int>(sampleIdx);
|
minClass = _labels.at<int>(sampleIdx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return minClass;
|
}
|
||||||
|
|
||||||
|
int Eigenfaces::predict(InputArray _src) const {
|
||||||
|
int label;
|
||||||
|
double dummy;
|
||||||
|
predict(_src, label, dummy);
|
||||||
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Eigenfaces::load(const FileStorage& fs) {
|
void Eigenfaces::load(const FileStorage& fs) {
|
||||||
@ -381,10 +408,10 @@ void Eigenfaces::save(FileStorage& fs) const {
|
|||||||
void Fisherfaces::train(InputArray src, InputArray _lbls) {
|
void Fisherfaces::train(InputArray src, InputArray _lbls) {
|
||||||
if(src.total() == 0) {
|
if(src.total() == 0) {
|
||||||
string error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
|
string error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
|
||||||
error(cv::Exception(CV_StsUnsupportedFormat, error_message, "cv::Eigenfaces::train", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
} else if(_lbls.getMat().type() != CV_32SC1) {
|
} else if(_lbls.getMat().type() != CV_32SC1) {
|
||||||
string error_message = format("Labels must be given as integer (CV_32SC1). Expected %d, but was %d.", CV_32SC1, _lbls.type());
|
string error_message = format("Labels must be given as integer (CV_32SC1). Expected %d, but was %d.", CV_32SC1, _lbls.type());
|
||||||
error(cv::Exception(CV_StsUnsupportedFormat, error_message, "cv::Fisherfaces::train", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// get data
|
// get data
|
||||||
Mat labels = _lbls.getMat();
|
Mat labels = _lbls.getMat();
|
||||||
@ -394,10 +421,10 @@ void Fisherfaces::train(InputArray src, InputArray _lbls) {
|
|||||||
// make sure labels are passed in correct shape
|
// make sure labels are passed in correct shape
|
||||||
if(labels.total() != (size_t) N) {
|
if(labels.total() != (size_t) N) {
|
||||||
string error_message = format("The number of samples (src) must equal the number of labels (labels)! len(src)=%d, len(labels)=%d.", N, labels.total());
|
string error_message = format("The number of samples (src) must equal the number of labels (labels)! len(src)=%d, len(labels)=%d.", N, labels.total());
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "Fisherfaces::train", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
} else if(labels.rows != 1 && labels.cols != 1) {
|
} else if(labels.rows != 1 && labels.cols != 1) {
|
||||||
string error_message = format("Expected the labels in a matrix with one row or column! Given dimensions are rows=%s, cols=%d.", labels.rows, labels.cols);
|
string error_message = format("Expected the labels in a matrix with one row or column! Given dimensions are rows=%s, cols=%d.", labels.rows, labels.cols);
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "Fisherfaces::train", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// Get the number of unique classes
|
// Get the number of unique classes
|
||||||
// TODO Provide a cv::Mat version?
|
// TODO Provide a cv::Mat version?
|
||||||
@ -427,32 +454,37 @@ void Fisherfaces::train(InputArray src, InputArray _lbls) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Fisherfaces::predict(InputArray _src) const {
|
void Fisherfaces::predict(InputArray _src, int &minClass, double &minDist) const {
|
||||||
Mat src = _src.getMat();
|
Mat src = _src.getMat();
|
||||||
// check data alignment just for clearer exception messages
|
// check data alignment just for clearer exception messages
|
||||||
if(_projections.empty()) {
|
if(_projections.empty()) {
|
||||||
// throw error if no data (or simply return -1?)
|
// throw error if no data (or simply return -1?)
|
||||||
string error_message = "This Fisherfaces model is not computed yet. Did you call Fisherfaces::train?";
|
string error_message = "This Fisherfaces model is not computed yet. Did you call Fisherfaces::train?";
|
||||||
error(cv::Exception(CV_StsError, error_message, "Fisherfaces::predict", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
} else if(src.total() != (size_t) _eigenvectors.rows) {
|
} else if(src.total() != (size_t) _eigenvectors.rows) {
|
||||||
string error_message = format("Wrong input image size. Reason: Training and Test images must be of equal size! Expected an image with %d elements, but got %d.", _eigenvectors.rows, src.total());
|
string error_message = format("Wrong input image size. Reason: Training and Test images must be of equal size! Expected an image with %d elements, but got %d.", _eigenvectors.rows, src.total());
|
||||||
error(cv::Exception(CV_StsError, error_message, "Fisherfaces::predict", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// project into LDA subspace
|
// project into LDA subspace
|
||||||
Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1));
|
Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1));
|
||||||
// find 1-nearest neighbor
|
// find 1-nearest neighbor
|
||||||
double minDist = DBL_MAX;
|
minDist = DBL_MAX;
|
||||||
int minClass = -1;
|
minClass = -1;
|
||||||
for(size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) {
|
for(size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) {
|
||||||
double dist = norm(_projections[sampleIdx], q, NORM_L2);
|
double dist = norm(_projections[sampleIdx], q, NORM_L2);
|
||||||
if(dist < minDist) {
|
if((dist < minDist) && (dist < _threshold)) {
|
||||||
minDist = dist;
|
minDist = dist;
|
||||||
minClass = _labels.at<int>(sampleIdx);
|
minClass = _labels.at<int>(sampleIdx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return minClass;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Fisherfaces::predict(InputArray _src) const {
|
||||||
|
int label;
|
||||||
|
double dummy;
|
||||||
|
predict(_src, label, dummy);
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
// See FaceRecognizer::load.
|
// See FaceRecognizer::load.
|
||||||
void Fisherfaces::load(const FileStorage& fs) {
|
void Fisherfaces::load(const FileStorage& fs) {
|
||||||
@ -675,13 +707,13 @@ void LBPH::save(FileStorage& fs) const {
|
|||||||
void LBPH::train(InputArray _src, InputArray _lbls) {
|
void LBPH::train(InputArray _src, InputArray _lbls) {
|
||||||
if(_src.kind() != _InputArray::STD_VECTOR_MAT && _src.kind() != _InputArray::STD_VECTOR_VECTOR) {
|
if(_src.kind() != _InputArray::STD_VECTOR_MAT && _src.kind() != _InputArray::STD_VECTOR_VECTOR) {
|
||||||
string error_message = "The images are expected as InputArray::STD_VECTOR_MAT (a std::vector<Mat>) or _InputArray::STD_VECTOR_VECTOR (a std::vector< vector<...> >).";
|
string error_message = "The images are expected as InputArray::STD_VECTOR_MAT (a std::vector<Mat>) or _InputArray::STD_VECTOR_VECTOR (a std::vector< vector<...> >).";
|
||||||
error(Exception(CV_StsBadArg, error_message, "LBPH::train", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
} else if(_src.total() == 0) {
|
} else if(_src.total() == 0) {
|
||||||
string error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
|
string error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
|
||||||
error(Exception(CV_StsUnsupportedFormat, error_message, "LBPH::train", __FILE__, __LINE__));
|
CV_Error(CV_StsUnsupportedFormat, error_message);
|
||||||
} else if(_lbls.getMat().type() != CV_32SC1) {
|
} else if(_lbls.getMat().type() != CV_32SC1) {
|
||||||
string error_message = format("Labels must be given as integer (CV_32SC1). Expected %d, but was %d.", CV_32SC1, _lbls.type());
|
string error_message = format("Labels must be given as integer (CV_32SC1). Expected %d, but was %d.", CV_32SC1, _lbls.type());
|
||||||
error(Exception(CV_StsUnsupportedFormat, error_message, "LBPH::train", __FILE__, __LINE__));
|
CV_Error(CV_StsUnsupportedFormat, error_message);
|
||||||
}
|
}
|
||||||
// get the vector of matrices
|
// get the vector of matrices
|
||||||
vector<Mat> src;
|
vector<Mat> src;
|
||||||
@ -689,8 +721,9 @@ void LBPH::train(InputArray _src, InputArray _lbls) {
|
|||||||
// turn the label matrix into a vector
|
// turn the label matrix into a vector
|
||||||
Mat labels = _lbls.getMat();
|
Mat labels = _lbls.getMat();
|
||||||
CV_Assert( labels.type() == CV_32S && (labels.cols == 1 || labels.rows == 1));
|
CV_Assert( labels.type() == CV_32S && (labels.cols == 1 || labels.rows == 1));
|
||||||
if(labels.total() != src.size())
|
if(labels.total() != src.size()) {
|
||||||
CV_Error(CV_StsUnsupportedFormat, "The number of labels must equal the number of samples.");
|
CV_Error(CV_StsUnsupportedFormat, "The number of labels must equal the number of samples.");
|
||||||
|
}
|
||||||
// store given labels
|
// store given labels
|
||||||
labels.copyTo(_labels);
|
labels.copyTo(_labels);
|
||||||
// store the spatial histograms of the original data
|
// store the spatial histograms of the original data
|
||||||
@ -709,7 +742,7 @@ void LBPH::train(InputArray _src, InputArray _lbls) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int LBPH::predict(InputArray _src) const {
|
void LBPH::predict(InputArray _src, int &minClass, double &minDist) const {
|
||||||
Mat src = _src.getMat();
|
Mat src = _src.getMat();
|
||||||
// get the spatial histogram from input image
|
// get the spatial histogram from input image
|
||||||
Mat lbp_image = elbp(src, _radius, _neighbors);
|
Mat lbp_image = elbp(src, _radius, _neighbors);
|
||||||
@ -720,37 +753,44 @@ int LBPH::predict(InputArray _src) const {
|
|||||||
_grid_y, /* grid size y */
|
_grid_y, /* grid size y */
|
||||||
true /* normed histograms */);
|
true /* normed histograms */);
|
||||||
// find 1-nearest neighbor
|
// find 1-nearest neighbor
|
||||||
double minDist = DBL_MAX;
|
minDist = DBL_MAX;
|
||||||
int minClass = -1;
|
minClass = -1;
|
||||||
for(size_t sampleIdx = 0; sampleIdx < _histograms.size(); sampleIdx++) {
|
for(size_t sampleIdx = 0; sampleIdx < _histograms.size(); sampleIdx++) {
|
||||||
double dist = compareHist(_histograms[sampleIdx], query, CV_COMP_CHISQR);
|
double dist = compareHist(_histograms[sampleIdx], query, CV_COMP_CHISQR);
|
||||||
if(dist < minDist) {
|
if((dist < minDist) && (dist < _threshold)) {
|
||||||
minDist = dist;
|
minDist = dist;
|
||||||
minClass = _labels.at<int>(sampleIdx);
|
minClass = _labels.at<int>(sampleIdx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return minClass;
|
}
|
||||||
|
|
||||||
|
int LBPH::predict(InputArray _src) const {
|
||||||
|
int label;
|
||||||
|
double dummy;
|
||||||
|
predict(_src, label, dummy);
|
||||||
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Ptr<FaceRecognizer> createEigenFaceRecognizer(int num_components)
|
Ptr<FaceRecognizer> createEigenFaceRecognizer(int num_components, double threshold)
|
||||||
{
|
{
|
||||||
return new Eigenfaces(num_components);
|
return new Eigenfaces(num_components, threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ptr<FaceRecognizer> createFisherFaceRecognizer(int num_components)
|
Ptr<FaceRecognizer> createFisherFaceRecognizer(int num_components, double threshold)
|
||||||
{
|
{
|
||||||
return new Fisherfaces(num_components);
|
return new Fisherfaces(num_components, threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ptr<FaceRecognizer> createLBPHFaceRecognizer(int radius, int neighbors,
|
Ptr<FaceRecognizer> createLBPHFaceRecognizer(int radius, int neighbors,
|
||||||
int grid_x, int grid_y)
|
int grid_x, int grid_y, double threshold)
|
||||||
{
|
{
|
||||||
return new LBPH(radius, neighbors, grid_x, grid_y);
|
return new LBPH(radius, neighbors, grid_x, grid_y, threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
CV_INIT_ALGORITHM(Eigenfaces, "FaceRecognizer.Eigenfaces",
|
CV_INIT_ALGORITHM(Eigenfaces, "FaceRecognizer.Eigenfaces",
|
||||||
obj.info()->addParam(obj, "ncomponents", obj._num_components);
|
obj.info()->addParam(obj, "ncomponents", obj._num_components);
|
||||||
|
obj.info()->addParam(obj, "threshold", obj._threshold);
|
||||||
obj.info()->addParam(obj, "projections", obj._projections, true);
|
obj.info()->addParam(obj, "projections", obj._projections, true);
|
||||||
obj.info()->addParam(obj, "labels", obj._labels, true);
|
obj.info()->addParam(obj, "labels", obj._labels, true);
|
||||||
obj.info()->addParam(obj, "eigenvectors", obj._eigenvectors, true);
|
obj.info()->addParam(obj, "eigenvectors", obj._eigenvectors, true);
|
||||||
@ -759,6 +799,7 @@ CV_INIT_ALGORITHM(Eigenfaces, "FaceRecognizer.Eigenfaces",
|
|||||||
|
|
||||||
CV_INIT_ALGORITHM(Fisherfaces, "FaceRecognizer.Fisherfaces",
|
CV_INIT_ALGORITHM(Fisherfaces, "FaceRecognizer.Fisherfaces",
|
||||||
obj.info()->addParam(obj, "ncomponents", obj._num_components);
|
obj.info()->addParam(obj, "ncomponents", obj._num_components);
|
||||||
|
obj.info()->addParam(obj, "threshold", obj._threshold);
|
||||||
obj.info()->addParam(obj, "projections", obj._projections, true);
|
obj.info()->addParam(obj, "projections", obj._projections, true);
|
||||||
obj.info()->addParam(obj, "labels", obj._labels, true);
|
obj.info()->addParam(obj, "labels", obj._labels, true);
|
||||||
obj.info()->addParam(obj, "eigenvectors", obj._eigenvectors, true);
|
obj.info()->addParam(obj, "eigenvectors", obj._eigenvectors, true);
|
||||||
@ -770,6 +811,7 @@ CV_INIT_ALGORITHM(LBPH, "FaceRecognizer.LBPH",
|
|||||||
obj.info()->addParam(obj, "neighbors", obj._neighbors);
|
obj.info()->addParam(obj, "neighbors", obj._neighbors);
|
||||||
obj.info()->addParam(obj, "grid_x", obj._grid_x);
|
obj.info()->addParam(obj, "grid_x", obj._grid_x);
|
||||||
obj.info()->addParam(obj, "grid_y", obj._grid_y);
|
obj.info()->addParam(obj, "grid_y", obj._grid_y);
|
||||||
|
obj.info()->addParam(obj, "threshold", obj._threshold);
|
||||||
obj.info()->addParam(obj, "histograms", obj._histograms, true);
|
obj.info()->addParam(obj, "histograms", obj._histograms, true);
|
||||||
obj.info()->addParam(obj, "labels", obj._labels, true));
|
obj.info()->addParam(obj, "labels", obj._labels, true));
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ static Mat argsort(InputArray _src, bool ascending=true)
|
|||||||
Mat src = _src.getMat();
|
Mat src = _src.getMat();
|
||||||
if (src.rows != 1 && src.cols != 1) {
|
if (src.rows != 1 && src.cols != 1) {
|
||||||
string error_message = "Wrong shape of input matrix! Expected a matrix with one row or column.";
|
string error_message = "Wrong shape of input matrix! Expected a matrix with one row or column.";
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "argsort", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
int flags = CV_SORT_EVERY_ROW+(ascending ? CV_SORT_ASCENDING : CV_SORT_DESCENDING);
|
int flags = CV_SORT_EVERY_ROW+(ascending ? CV_SORT_ASCENDING : CV_SORT_DESCENDING);
|
||||||
Mat sorted_indices;
|
Mat sorted_indices;
|
||||||
@ -60,7 +60,7 @@ static Mat asRowMatrix(InputArrayOfArrays src, int rtype, double alpha=1, double
|
|||||||
// make sure the input data is a vector of matrices or vector of vector
|
// 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) {
|
if(src.kind() != _InputArray::STD_VECTOR_MAT && src.kind() != _InputArray::STD_VECTOR_VECTOR) {
|
||||||
string error_message = "The data is expected as InputArray::STD_VECTOR_MAT (a std::vector<Mat>) or _InputArray::STD_VECTOR_VECTOR (a std::vector< vector<...> >).";
|
string error_message = "The data is expected as InputArray::STD_VECTOR_MAT (a std::vector<Mat>) or _InputArray::STD_VECTOR_VECTOR (a std::vector< vector<...> >).";
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "asRowMatrix", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// number of samples
|
// number of samples
|
||||||
size_t n = src.total();
|
size_t n = src.total();
|
||||||
@ -76,7 +76,7 @@ static Mat asRowMatrix(InputArrayOfArrays src, int rtype, double alpha=1, double
|
|||||||
// make sure data can be reshaped, throw exception if not!
|
// make sure data can be reshaped, throw exception if not!
|
||||||
if(src.getMat(i).total() != d) {
|
if(src.getMat(i).total() != d) {
|
||||||
string error_message = format("Wrong number of elements in matrix #%d! Expected %d was %d.", i, d, src.getMat(i).total());
|
string error_message = format("Wrong number of elements in matrix #%d! Expected %d was %d.", i, d, src.getMat(i).total());
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "cv::asRowMatrix", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// get a hold of the current row
|
// get a hold of the current row
|
||||||
Mat xi = data.row(i);
|
Mat xi = data.row(i);
|
||||||
@ -91,8 +91,9 @@ static Mat asRowMatrix(InputArrayOfArrays src, int rtype, double alpha=1, double
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void sortMatrixColumnsByIndices(InputArray _src, InputArray _indices, OutputArray _dst) {
|
static void sortMatrixColumnsByIndices(InputArray _src, InputArray _indices, OutputArray _dst) {
|
||||||
if(_indices.getMat().type() != CV_32SC1)
|
if(_indices.getMat().type() != CV_32SC1) {
|
||||||
CV_Error(CV_StsUnsupportedFormat, "cv::sortColumnsByIndices only works on integer indices!");
|
CV_Error(CV_StsUnsupportedFormat, "cv::sortColumnsByIndices only works on integer indices!");
|
||||||
|
}
|
||||||
Mat src = _src.getMat();
|
Mat src = _src.getMat();
|
||||||
vector<int> indices = _indices.getMat();
|
vector<int> indices = _indices.getMat();
|
||||||
_dst.create(src.rows, src.cols, src.type());
|
_dst.create(src.rows, src.cols, src.type());
|
||||||
@ -183,12 +184,12 @@ Mat subspaceProject(InputArray _W, InputArray _mean, InputArray _src) {
|
|||||||
// make sure the data has the correct shape
|
// make sure the data has the correct shape
|
||||||
if(W.rows != d) {
|
if(W.rows != d) {
|
||||||
string error_message = format("Wrong shapes for given matrices. Was size(src) = (%d,%d), size(W) = (%d,%d).", src.rows, src.cols, W.rows, W.cols);
|
string error_message = format("Wrong shapes for given matrices. Was size(src) = (%d,%d), size(W) = (%d,%d).", src.rows, src.cols, W.rows, W.cols);
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "cv::subspace::project", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// make sure mean is correct if not empty
|
// make sure mean is correct if not empty
|
||||||
if(!mean.empty() && (mean.total() != (size_t) d)) {
|
if(!mean.empty() && (mean.total() != (size_t) d)) {
|
||||||
string error_message = format("Wrong mean shape for the given data matrix. Expected %d, but was %d.", d, mean.total());
|
string error_message = format("Wrong mean shape for the given data matrix. Expected %d, but was %d.", d, mean.total());
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "cv::subspace::project", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// create temporary matrices
|
// create temporary matrices
|
||||||
Mat X, Y;
|
Mat X, Y;
|
||||||
@ -221,12 +222,12 @@ Mat subspaceReconstruct(InputArray _W, InputArray _mean, InputArray _src)
|
|||||||
// make sure the data has the correct shape
|
// make sure the data has the correct shape
|
||||||
if(W.cols != d) {
|
if(W.cols != d) {
|
||||||
string error_message = format("Wrong shapes for given matrices. Was size(src) = (%d,%d), size(W) = (%d,%d).", src.rows, src.cols, W.rows, W.cols);
|
string error_message = format("Wrong shapes for given matrices. Was size(src) = (%d,%d), size(W) = (%d,%d).", src.rows, src.cols, W.rows, W.cols);
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "cv::subspaceReconstruct", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// make sure mean is correct if not empty
|
// make sure mean is correct if not empty
|
||||||
if(!mean.empty() && (mean.total() != (size_t) W.rows)) {
|
if(!mean.empty() && (mean.total() != (size_t) W.rows)) {
|
||||||
string error_message = format("Wrong mean shape for the given eigenvector matrix. Expected %d, but was %d.", W.cols, mean.total());
|
string error_message = format("Wrong mean shape for the given eigenvector matrix. Expected %d, but was %d.", W.cols, mean.total());
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "cv::subspaceReconstruct", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// initalize temporary matrices
|
// initalize temporary matrices
|
||||||
Mat X, Y;
|
Mat X, Y;
|
||||||
@ -999,12 +1000,12 @@ void LDA::lda(InputArray _src, InputArray _lbls) {
|
|||||||
// want to separate from each other then?
|
// want to separate from each other then?
|
||||||
if(C == 1) {
|
if(C == 1) {
|
||||||
string error_message = "At least two classes are needed to perform a LDA. Reason: Only one class was given!";
|
string error_message = "At least two classes are needed to perform a LDA. Reason: Only one class was given!";
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "cv::LDA::lda", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// throw error if less labels, than samples
|
// throw error if less labels, than samples
|
||||||
if (labels.size() != static_cast<size_t>(N)) {
|
if (labels.size() != static_cast<size_t>(N)) {
|
||||||
string error_message = format("The number of samples must equal the number of labels. Given %d labels, %d samples. ", labels.size(), N);
|
string error_message = format("The number of samples must equal the number of labels. Given %d labels, %d samples. ", labels.size(), N);
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "LDA::lda", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
}
|
}
|
||||||
// warn if within-classes scatter matrix becomes singular
|
// warn if within-classes scatter matrix becomes singular
|
||||||
if (N < D) {
|
if (N < D) {
|
||||||
@ -1087,7 +1088,7 @@ void LDA::compute(InputArray _src, InputArray _lbls) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
string error_message= format("InputArray Datatype %d is not supported.", _src.kind());
|
string error_message= format("InputArray Datatype %d is not supported.", _src.kind());
|
||||||
error(cv::Exception(CV_StsBadArg, error_message, "LDA::compute", __FILE__, __LINE__));
|
CV_Error(CV_StsBadArg, error_message);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,24 +103,45 @@ int main(int argc, const char *argv[]) {
|
|||||||
// the factory method like this:
|
// the factory method like this:
|
||||||
//
|
//
|
||||||
// cv::createEigenFaceRecognizer(10);
|
// cv::createEigenFaceRecognizer(10);
|
||||||
|
//
|
||||||
|
// If you want to create a FaceRecognizer with a
|
||||||
|
// confidennce threshold, call it with:
|
||||||
|
//
|
||||||
|
// cv::createEigenFaceRecognizer(10, 123.0);
|
||||||
|
//
|
||||||
Ptr<FaceRecognizer> model = createEigenFaceRecognizer();
|
Ptr<FaceRecognizer> model = createEigenFaceRecognizer();
|
||||||
model->train(images, labels);
|
model->train(images, labels);
|
||||||
// The following line predicts the label of a given
|
// The following line predicts the label of a given
|
||||||
// test image. In this example no thresholding is
|
// test image:
|
||||||
// done.
|
int predictedLabel = model->predict(testSample);
|
||||||
int predicted = model->predict(testSample);
|
//
|
||||||
// Show the prediction and actual class of the given
|
// To get the confidence of a prediction call it with:
|
||||||
// sample:
|
//
|
||||||
string result_message = format("Predicted class=%d / Actual class=%d.", predicted, testLabel);
|
// model with:
|
||||||
|
// int predictedLabel = -1;
|
||||||
|
// double confidence = 0.0;
|
||||||
|
// model->predict(testSample, predictedLabel, confidence);
|
||||||
|
//
|
||||||
|
string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
|
||||||
cout << result_message << endl;
|
cout << result_message << endl;
|
||||||
// Sometimes you'll need to get some internal model data,
|
// Sometimes you'll need to get/set internal model data,
|
||||||
// which isn't exposed by the public cv::FaceRecognizer.
|
// which isn't exposed by the public cv::FaceRecognizer.
|
||||||
// Since each cv::FaceRecognizer is derived from a
|
// Since each cv::FaceRecognizer is derived from a
|
||||||
// cv::Algorithm, you can query the data.
|
// cv::Algorithm, you can query the data.
|
||||||
//
|
//
|
||||||
// Here is how to get the eigenvalues of this Eigenfaces model:
|
// First we'll use it to set the threshold of the FaceRecognizer
|
||||||
|
// without retraining the model:
|
||||||
|
//
|
||||||
|
model->set("threshold", 0.0);
|
||||||
|
// Now the threshold is of this model is 0.0. A prediction
|
||||||
|
// now returns -1, as it's impossible to have a distance
|
||||||
|
// below it
|
||||||
|
//
|
||||||
|
predictedLabel = model->predict(testSample);
|
||||||
|
cout << "Predicted class = " << predictedLabel << endl;
|
||||||
|
// Now here is how to get the eigenvalues of this Eigenfaces model:
|
||||||
Mat eigenvalues = model->getMat("eigenvalues");
|
Mat eigenvalues = model->getMat("eigenvalues");
|
||||||
// And we can do the same to display the Eigenvectors ("Eigenfaces"):
|
// And we can do the same to display the Eigenvectors (read Eigenfaces):
|
||||||
Mat W = model->getMat("eigenvectors");
|
Mat W = model->getMat("eigenvectors");
|
||||||
// From this we will display the (at most) first 10 Eigenfaces:
|
// From this we will display the (at most) first 10 Eigenfaces:
|
||||||
for (int i = 0; i < min(10, W.cols); i++) {
|
for (int i = 0; i < min(10, W.cols); i++) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user