Exceptions now go into CV_Error. Added thresholding to the FaceRecognizer and updated...
authorPhilipp Wagner <no@email>
Sun, 10 Jun 2012 22:23:18 +0000 (22:23 +0000)
committerPhilipp Wagner <no@email>
Sun, 10 Jun 2012 22:23:18 +0000 (22:23 +0000)
modules/contrib/include/opencv2/contrib/contrib.hpp
modules/contrib/src/facerec.cpp
modules/contrib/src/lda.cpp
samples/cpp/facerec_demo.cpp

index 6a95b7a..20947ec 100644 (file)
@@ -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<FaceRecognizer> createEigenFaceRecognizer(int num_components = 0);
-    CV_EXPORTS Ptr<FaceRecognizer> createFisherFaceRecognizer(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, double threshold = DBL_MAX);
     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
     {
index 6d61fb3..1bf7f9b 100644 (file)
@@ -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<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(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<Mat> _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<Mat> _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<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());
-        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<int>(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<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) {
@@ -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<int>(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<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) {
         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<Mat> 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<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,
-                                             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));
     
index f3864ea..2abfabd 100644 (file)
@@ -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<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
     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<int> 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<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);
-        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;
     }
 }
index c329cc4..d252ba1 100644 (file)
@@ -41,7 +41,7 @@ static Mat toGrayscale(InputArray _src) {
 static void read_csv(const string& filename, vector<Mat>& images, vector<int>& 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<Mat>& images, vector<int>& 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] << " <csv.ext>" << 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<FaceRecognizer> 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<double>(i));
-       cout << msg << endl;
+        string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
+        cout << msg << endl;
         // get eigenvector #i
         Mat ev = W.col(i).clone();
         // Reshape to original size & normalize to [0...255] for imshow.