You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
5.7 KiB
139 lines
5.7 KiB
/* |
|
* Copyright (c) 2011,2012. Philipp Wagner <bytefish[at]gmx[dot]de>. |
|
* Released to public domain under terms of the BSD Simplified license. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions are met: |
|
* * Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* * Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* * Neither the name of the organization nor the names of its contributors |
|
* may be used to endorse or promote products derived from this software |
|
* without specific prior written permission. |
|
* |
|
* See <http://www.opensource.org/licenses/bsd-license> |
|
*/ |
|
#include "precomp.hpp" |
|
#include <opencv2/face.hpp> |
|
#include "face_utils.hpp" |
|
#include <set> |
|
#include <limits> |
|
#include <iostream> |
|
|
|
namespace cv |
|
{ |
|
namespace face |
|
{ |
|
|
|
// Turk, M., and Pentland, A. "Eigenfaces for recognition.". Journal of |
|
// Cognitive Neuroscience 3 (1991), 71–86. |
|
class Eigenfaces : public EigenFaceRecognizer |
|
{ |
|
|
|
public: |
|
// Initializes an empty Eigenfaces model. |
|
Eigenfaces(int num_components = 0, double threshold = DBL_MAX) |
|
//: BasicFaceRecognizerImpl(num_components, threshold) |
|
{ |
|
_num_components = num_components; |
|
_threshold = threshold; |
|
} |
|
|
|
// Computes an Eigenfaces model with images in src and corresponding labels |
|
// in labels. |
|
void train(InputArrayOfArrays src, InputArray labels) CV_OVERRIDE; |
|
|
|
// Send all predict results to caller side for custom result handling |
|
void predict(InputArray src, Ptr<PredictCollector> collector) const CV_OVERRIDE; |
|
String getDefaultName() const CV_OVERRIDE |
|
{ |
|
return "opencv_eigenfaces"; |
|
} |
|
}; |
|
|
|
//------------------------------------------------------------------------------ |
|
// Eigenfaces |
|
//------------------------------------------------------------------------------ |
|
void Eigenfaces::train(InputArrayOfArrays _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."); |
|
CV_Error(Error::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()); |
|
CV_Error(Error::StsBadArg, error_message); |
|
} |
|
// make sure data has correct size |
|
if(_src.total() > 1) { |
|
for(int i = 1; i < static_cast<int>(_src.total()); i++) { |
|
if(_src.getMat(i-1).total() != _src.getMat(i).total()) { |
|
String error_message = format("In the Eigenfaces method all input samples (training images) must be of equal size! Expected %zu pixels, but was %zu pixels.", _src.getMat(i-1).total(), _src.getMat(i).total()); |
|
CV_Error(Error::StsUnsupportedFormat, error_message); |
|
} |
|
} |
|
} |
|
// get labels |
|
Mat labels = _local_labels.getMat(); |
|
// observations in row |
|
Mat data = asRowMatrix(_src, CV_64FC1); |
|
|
|
// number of samples |
|
int n = data.rows; |
|
// 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)=%zu.", n, labels.total()); |
|
CV_Error(Error::StsBadArg, error_message); |
|
} |
|
// clear existing model data |
|
_labels.release(); |
|
_projections.clear(); |
|
// clip number of components to be valid |
|
if((_num_components <= 0) || (_num_components > n)) |
|
_num_components = n; |
|
|
|
// perform the PCA |
|
PCA pca(data, Mat(), PCA::DATA_AS_ROW, _num_components); |
|
// copy the PCA results |
|
_mean = pca.mean.reshape(1,1); // store the mean vector |
|
_eigenvalues = pca.eigenvalues.clone(); // eigenvalues by row |
|
transpose(pca.eigenvectors, _eigenvectors); // eigenvectors by column |
|
// store labels for prediction |
|
_labels = labels.clone(); |
|
// save projections |
|
for(int sampleIdx = 0; sampleIdx < data.rows; sampleIdx++) { |
|
Mat p = LDA::subspaceProject(_eigenvectors, _mean, data.row(sampleIdx)); |
|
_projections.push_back(p); |
|
} |
|
} |
|
|
|
void Eigenfaces::predict(InputArray _src, Ptr<PredictCollector> collector) 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?"; |
|
CV_Error(Error::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 %zu.", _eigenvectors.rows, src.total()); |
|
CV_Error(Error::StsBadArg, error_message); |
|
} |
|
// project into PCA subspace |
|
Mat q = LDA::subspaceProject(_eigenvectors, _mean, src.reshape(1, 1)); |
|
collector->init(_projections.size()); |
|
for (size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) { |
|
double dist = norm(_projections[sampleIdx], q, NORM_L2); |
|
int label = _labels.at<int>((int)sampleIdx); |
|
if (!collector->collect(label, dist))return; |
|
} |
|
} |
|
|
|
Ptr<EigenFaceRecognizer> EigenFaceRecognizer::create(int num_components, double threshold) |
|
{ |
|
return makePtr<Eigenfaces>(num_components, threshold); |
|
} |
|
|
|
} |
|
}
|
|
|