Commit cd7d93f3 authored by Philipp Wagner's avatar Philipp Wagner

Exceptions now go into CV_Error. Added thresholding to the FaceRecognizer and…

Exceptions now go into CV_Error. Added thresholding to the FaceRecognizer and updated the demo accordingly.
parent ee1b6712
...@@ -930,6 +930,9 @@ namespace cv ...@@ -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 ...@@ -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
{ {
......
This diff is collapsed.
...@@ -48,7 +48,7 @@ static Mat argsort(InputArray _src, bool ascending=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 ...@@ -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 ...@@ -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 ...@@ -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) { ...@@ -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) ...@@ -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) { ...@@ -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) { ...@@ -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;
} }
} }
......
...@@ -41,7 +41,7 @@ static Mat toGrayscale(InputArray _src) { ...@@ -41,7 +41,7 @@ static Mat toGrayscale(InputArray _src) {
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') { static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
std::ifstream file(filename.c_str(), ifstream::in); std::ifstream file(filename.c_str(), ifstream::in);
if (!file) { 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); CV_Error(CV_StsBadArg, error_message);
} }
string line, path, classlabel; string line, path, classlabel;
...@@ -58,7 +58,7 @@ static void read_csv(const string& filename, vector<Mat>& images, vector<int>& l ...@@ -58,7 +58,7 @@ static void read_csv(const string& filename, vector<Mat>& images, vector<int>& l
int main(int argc, const char *argv[]) { int main(int argc, const char *argv[]) {
// Check for valid command line arguments, print usage // Check for valid command line arguments, print usage
// if no arguments were given. // if no arguments were given.
if (argc != 2) { if (argc != 2) {
cout << "usage: " << argv[0] << " <csv.ext>" << endl; cout << "usage: " << argv[0] << " <csv.ext>" << endl;
exit(1); exit(1);
...@@ -79,8 +79,8 @@ int main(int argc, const char *argv[]) { ...@@ -79,8 +79,8 @@ int main(int argc, const char *argv[]) {
} }
// Quit if there are not enough images for this demo. // Quit if there are not enough images for this demo.
if(images.size() <= 1) { if(images.size() <= 1) {
string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!"; 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); CV_Error(CV_StsError, error_message);
} }
// Get the height from the first image. We'll need this // Get the height from the first image. We'll need this
// later in code to reshape the images to their original // later in code to reshape the images to their original
...@@ -102,30 +102,51 @@ int main(int argc, const char *argv[]) { ...@@ -102,30 +102,51 @@ int main(int argc, const char *argv[]) {
// 10 principal components (read Eigenfaces), then call // 10 principal components (read Eigenfaces), then call
// 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++) {
string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i)); string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
cout << msg << endl; cout << msg << endl;
// get eigenvector #i // get eigenvector #i
Mat ev = W.col(i).clone(); Mat ev = W.col(i).clone();
// Reshape to original size & normalize to [0...255] for imshow. // Reshape to original size & normalize to [0...255] for imshow.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment