From cd7d93f3623ee13d94b39e8bf96f0617cd83196a Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sun, 10 Jun 2012 22:23:18 +0000 Subject: [PATCH] Exceptions now go into CV_Error. Added thresholding to the FaceRecognizer and updated the demo accordingly. --- .../contrib/include/opencv2/contrib/contrib.hpp | 9 +- modules/contrib/src/facerec.cpp | 144 +++++++++++++-------- modules/contrib/src/lda.cpp | 23 ++-- samples/cpp/facerec_demo.cpp | 61 ++++++--- 4 files changed, 152 insertions(+), 85 deletions(-) diff --git a/modules/contrib/include/opencv2/contrib/contrib.hpp b/modules/contrib/include/opencv2/contrib/contrib.hpp index 6a95b7a..20947ec 100644 --- a/modules/contrib/include/opencv2/contrib/contrib.hpp +++ b/modules/contrib/include/opencv2/contrib/contrib.hpp @@ -930,6 +930,9 @@ namespace cv // Gets a prediction from a FaceRecognizer. 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. virtual void save(const string& filename) const; @@ -944,10 +947,10 @@ namespace cv }; - CV_EXPORTS Ptr createEigenFaceRecognizer(int num_components = 0); - CV_EXPORTS Ptr createFisherFaceRecognizer(int num_components = 0); + CV_EXPORTS Ptr createEigenFaceRecognizer(int num_components = 0, double threshold = DBL_MAX); + CV_EXPORTS Ptr createFisherFaceRecognizer(int num_components = 0, double threshold = DBL_MAX); CV_EXPORTS Ptr 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 { diff --git a/modules/contrib/src/facerec.cpp b/modules/contrib/src/facerec.cpp index 6d61fb3..1bf7f9b 100644 --- a/modules/contrib/src/facerec.cpp +++ b/modules/contrib/src/facerec.cpp @@ -52,7 +52,7 @@ inline void writeFileNodeList(FileStorage& fs, const string& name, static 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) { - string error_message = "The data is expected as InputArray::STD_VECTOR_MAT (a std::vector) or _InputArray::STD_VECTOR_VECTOR (a std::vector< vector<...> >)."; + string error_message = "The data is expected as InputArray::STD_VECTOR_MAT (a std::vector) or _InputArray::STD_VECTOR_VECTOR (a std::vector< vector<...> >)."; error(Exception(CV_StsBadArg, error_message, "asRowMatrix", __FILE__, __LINE__)); } // number of samples @@ -105,6 +105,7 @@ class Eigenfaces : public FaceRecognizer { private: int _num_components; + double _threshold; vector _projections; Mat _labels; Mat _eigenvectors; @@ -116,15 +117,18 @@ public: using FaceRecognizer::load; // Initializes an empty Eigenfaces model. - Eigenfaces(int num_components = 0) : - _num_components(num_components) { } + Eigenfaces(int num_components = 0, double threshold = DBL_MAX) : + _num_components(num_components), + _threshold(threshold) {} // Initializes and computes an Eigenfaces model with images in src and // corresponding labels in labels. num_components will be kept for // classification. Eigenfaces(InputArray src, InputArray labels, - int num_components = 0) : - _num_components(num_components) { + int num_components = 0, + double threshold = DBL_MAX) : + _num_components(num_components), + _threshold(threshold) { train(src, labels); } @@ -135,6 +139,9 @@ public: // Predicts the label of a query image in src. 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. void load(const FileStorage& fs); @@ -152,6 +159,7 @@ class Fisherfaces: public FaceRecognizer { private: int _num_components; + double _threshold; Mat _eigenvectors; Mat _eigenvalues; Mat _mean; @@ -163,16 +171,19 @@ public: using FaceRecognizer::load; // Initializes an empty Fisherfaces model. - Fisherfaces(int num_components = 0) : - _num_components(num_components) {} + Fisherfaces(int num_components = 0, double threshold = DBL_MAX) : + _num_components(num_components), + _threshold(threshold) {} // Initializes and computes a Fisherfaces model with images in src and // corresponding labels in labels. num_components will be kept for // classification. Fisherfaces(InputArray src, InputArray labels, - int num_components = 0) : - _num_components(num_components) { + int num_components = 0, + double threshold = DBL_MAX) : + _num_components(num_components), + _threshold(threshold) { train(src, labels); } @@ -185,6 +196,9 @@ public: // Predicts the label of a query image in src. 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. virtual void load(const FileStorage& fs); @@ -207,6 +221,7 @@ private: int _grid_y; int _radius; int _neighbors; + double _threshold; vector _histograms; Mat _labels; @@ -220,11 +235,12 @@ public: // // radius, neighbors are used in the local binary patterns creation. // 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_y(grid_y), _radius(radius), - _neighbors(neighbors) {} + _neighbors(neighbors), + _threshold(threshold) {} // Initializes and computes this LBPH Model. The current implementation is // rather fixed as it uses the Extended Local Binary Patterns per default. @@ -234,11 +250,13 @@ public: LBPH(InputArray src, InputArray labels, 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_y(grid_y), _radius(radius), - _neighbors(neighbors) { + _neighbors(neighbors), + _threshold(threshold) { train(src, labels); } @@ -251,6 +269,9 @@ public: // Predicts the label of a query image in src. 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. void load(const FileStorage& fs); @@ -293,10 +314,10 @@ void FaceRecognizer::load(const string& filename) { void Eigenfaces::train(InputArray _src, InputArray _local_labels) { if(_src.total() == 0) { 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) { 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 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 if(static_cast(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()); - error(Exception(CV_StsBadArg, error_message, "Eigenfaces::train", __FILE__, __LINE__)); + CV_Error(CV_StsBadArg, error_message); } // clip number of components to be valid 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 Mat src = _src.getMat(); // make sure the user is passing correct data if(_projections.empty()) { // throw error if no data (or simply return -1?) 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(src.total())) { // 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()); - error(cv::Exception(CV_StsError, error_message, "Eigenfaces::predict", __FILE__, __LINE__)); + CV_Error(CV_StsBadArg, error_message); } // project into PCA subspace Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1)); - double minDist = DBL_MAX; - int minClass = -1; + minDist = DBL_MAX; + minClass = -1; for(size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) { double dist = norm(_projections[sampleIdx], q, NORM_L2); - if(dist < minDist) { + if((dist < minDist) && (dist < _threshold)) { minDist = dist; minClass = _labels.at(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) { @@ -381,10 +408,10 @@ void Eigenfaces::save(FileStorage& fs) const { void Fisherfaces::train(InputArray src, InputArray _lbls) { if(src.total() == 0) { 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) { 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 Mat labels = _lbls.getMat(); @@ -393,11 +420,11 @@ void Fisherfaces::train(InputArray src, InputArray _lbls) { int N = data.rows; // make sure labels are passed in correct shape 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()); - error(cv::Exception(CV_StsBadArg, error_message, "Fisherfaces::train", __FILE__, __LINE__)); + 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()); + CV_Error(CV_StsBadArg, error_message); } 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); - error(cv::Exception(CV_StsBadArg, error_message, "Fisherfaces::train", __FILE__, __LINE__)); + CV_Error(CV_StsBadArg, error_message); } // Get the number of unique classes // 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(); // check data alignment just for clearer exception messages if(_projections.empty()) { // throw error if no data (or simply return -1?) 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) { 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 Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1)); // find 1-nearest neighbor - double minDist = DBL_MAX; - int minClass = -1; + minDist = DBL_MAX; + minClass = -1; for(size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) { double dist = norm(_projections[sampleIdx], q, NORM_L2); - if(dist < minDist) { + if((dist < minDist) && (dist < _threshold)) { minDist = dist; minClass = _labels.at(sampleIdx); } } - return minClass; } +int Fisherfaces::predict(InputArray _src) const { + int label; + double dummy; + predict(_src, label, dummy); + return label; +} // See FaceRecognizer::load. void Fisherfaces::load(const FileStorage& fs) { @@ -675,13 +707,13 @@ void LBPH::save(FileStorage& fs) const { void LBPH::train(InputArray _src, InputArray _lbls) { 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) 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) { 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) { 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 vector src; @@ -689,8 +721,9 @@ void LBPH::train(InputArray _src, InputArray _lbls) { // turn the label matrix into a vector Mat labels = _lbls.getMat(); 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."); + } // store given labels labels.copyTo(_labels); // 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(); // get the spatial histogram from input image Mat lbp_image = elbp(src, _radius, _neighbors); @@ -720,37 +753,44 @@ int LBPH::predict(InputArray _src) const { _grid_y, /* grid size y */ true /* normed histograms */); // find 1-nearest neighbor - double minDist = DBL_MAX; - int minClass = -1; + minDist = DBL_MAX; + minClass = -1; for(size_t sampleIdx = 0; sampleIdx < _histograms.size(); sampleIdx++) { double dist = compareHist(_histograms[sampleIdx], query, CV_COMP_CHISQR); - if(dist < minDist) { + if((dist < minDist) && (dist < _threshold)) { minDist = dist; minClass = _labels.at(sampleIdx); } } - return minClass; } + +int LBPH::predict(InputArray _src) const { + int label; + double dummy; + predict(_src, label, dummy); + return label; +} + - -Ptr createEigenFaceRecognizer(int num_components) +Ptr createEigenFaceRecognizer(int num_components, double threshold) { - return new Eigenfaces(num_components); + return new Eigenfaces(num_components, threshold); } -Ptr createFisherFaceRecognizer(int num_components) +Ptr createFisherFaceRecognizer(int num_components, double threshold) { - return new Fisherfaces(num_components); + return new Fisherfaces(num_components, threshold); } Ptr 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", 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, "labels", obj._labels, 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", 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, "labels", obj._labels, 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, "grid_x", obj._grid_x); 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, "labels", obj._labels, true)); diff --git a/modules/contrib/src/lda.cpp b/modules/contrib/src/lda.cpp index f3864ea..2abfabd 100644 --- a/modules/contrib/src/lda.cpp +++ b/modules/contrib/src/lda.cpp @@ -48,7 +48,7 @@ static Mat argsort(InputArray _src, bool ascending=true) Mat src = _src.getMat(); if (src.rows != 1 && src.cols != 1) { 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); 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 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) 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 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! 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()); - error(cv::Exception(CV_StsBadArg, error_message, "cv::asRowMatrix", __FILE__, __LINE__)); + CV_Error(CV_StsBadArg, error_message); } // get a hold of the current row 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) { - if(_indices.getMat().type() != CV_32SC1) + if(_indices.getMat().type() != CV_32SC1) { CV_Error(CV_StsUnsupportedFormat, "cv::sortColumnsByIndices only works on integer indices!"); + } Mat src = _src.getMat(); vector indices = _indices.getMat(); _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 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); - 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 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()); - error(cv::Exception(CV_StsBadArg, error_message, "cv::subspace::project", __FILE__, __LINE__)); + CV_Error(CV_StsBadArg, error_message); } // create temporary matrices Mat X, Y; @@ -221,12 +222,12 @@ Mat subspaceReconstruct(InputArray _W, InputArray _mean, InputArray _src) // make sure the data has the correct shape 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); - 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 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()); - error(cv::Exception(CV_StsBadArg, error_message, "cv::subspaceReconstruct", __FILE__, __LINE__)); + CV_Error(CV_StsBadArg, error_message); } // initalize temporary matrices Mat X, Y; @@ -999,12 +1000,12 @@ void LDA::lda(InputArray _src, InputArray _lbls) { // want to separate from each other then? if(C == 1) { 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 if (labels.size() != static_cast(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 if (N < D) { @@ -1087,7 +1088,7 @@ void LDA::compute(InputArray _src, InputArray _lbls) { break; default: 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; } } diff --git a/samples/cpp/facerec_demo.cpp b/samples/cpp/facerec_demo.cpp index c329cc4..d252ba1 100644 --- a/samples/cpp/facerec_demo.cpp +++ b/samples/cpp/facerec_demo.cpp @@ -41,7 +41,7 @@ static Mat toGrayscale(InputArray _src) { static void read_csv(const string& filename, vector& images, vector& labels, char separator = ';') { std::ifstream file(filename.c_str(), ifstream::in); if (!file) { - string error_message = "No valid input file was given, please check the given filename."; + string error_message = "No valid input file was given, please check the given filename."; CV_Error(CV_StsBadArg, error_message); } string line, path, classlabel; @@ -58,7 +58,7 @@ static void read_csv(const string& filename, vector& images, vector& l int main(int argc, const char *argv[]) { // Check for valid command line arguments, print usage - // if no arguments were given. + // if no arguments were given. if (argc != 2) { cout << "usage: " << argv[0] << " " << endl; exit(1); @@ -79,8 +79,8 @@ int main(int argc, const char *argv[]) { } // Quit if there are not enough images for this demo. if(images.size() <= 1) { - string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!"; - CV_Error(CV_StsError, error_message); + string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!"; + CV_Error(CV_StsError, error_message); } // Get the height from the first image. We'll need this // later in code to reshape the images to their original @@ -102,30 +102,51 @@ int main(int argc, const char *argv[]) { // 10 principal components (read Eigenfaces), then call // 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 model = createEigenFaceRecognizer(); model->train(images, labels); // The following line predicts the label of a given - // test image. In this example no thresholding is - // done. - int predicted = model->predict(testSample); - // Show the prediction and actual class of the given - // sample: - string result_message = format("Predicted class=%d / Actual class=%d.", predicted, testLabel); + // test image: + int predictedLabel = model->predict(testSample); + // + // To get the confidence of a prediction call it with: + // + // 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; - // Sometimes you'll need to get some internal model data, - // which isn't exposed by the public cv::FaceRecognizer. - // Since each cv::FaceRecognizer is derived from a - // cv::Algorithm, you can query the data. - // - // Here is how to get the eigenvalues of this Eigenfaces model: + // Sometimes you'll need to get/set internal model data, + // which isn't exposed by the public cv::FaceRecognizer. + // Since each cv::FaceRecognizer is derived from a + // cv::Algorithm, you can query the data. + // + // 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"); - // 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"); // From this we will display the (at most) first 10 Eigenfaces: for (int i = 0; i < min(10, W.cols); i++) { - string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at(i)); - cout << msg << endl; + string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at(i)); + cout << msg << endl; // get eigenvector #i Mat ev = W.col(i).clone(); // Reshape to original size & normalize to [0...255] for imshow. -- 2.7.4