2 * Copyright (c) 2011,2012. Philipp Wagner <bytefish[at]gmx[dot]de>.
3 * Released to public domain under terms of the BSD Simplified license.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the organization nor the names of its contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
16 * See <http://www.opensource.org/licenses/bsd-license>
18 #include "precomp.hpp"
25 // Reads a sequence from a FileNode::SEQ with type _Tp into a result vector.
26 template<typename _Tp>
27 inline void readFileNodeList(const FileNode& fn, std::vector<_Tp>& result) {
28 if (fn.type() == FileNode::SEQ) {
29 for (FileNodeIterator it = fn.begin(); it != fn.end();) {
32 result.push_back(item);
37 // Writes the a list of given items to a cv::FileStorage.
38 template<typename _Tp>
39 inline void writeFileNodeList(FileStorage& fs, const String& name,
40 const std::vector<_Tp>& items) {
42 typedef typename std::vector<_Tp>::const_iterator constVecIterator;
43 // write the elements in item to fs
45 for (constVecIterator it = items.begin(); it != items.end(); ++it) {
51 static Mat asRowMatrix(InputArrayOfArrays src, int rtype, double alpha=1, double beta=0) {
52 // make sure the input data is a vector of matrices or vector of vector
53 if(src.kind() != _InputArray::STD_VECTOR_MAT && src.kind() != _InputArray::STD_VECTOR_VECTOR) {
54 String error_message = "The data is expected as InputArray::STD_VECTOR_MAT (a std::vector<Mat>) or _InputArray::STD_VECTOR_VECTOR (a std::vector< std::vector<...> >).";
55 CV_Error(Error::StsBadArg, error_message);
58 size_t n = src.total();
59 // return empty matrix if no matrices given
62 // dimensionality of (reshaped) samples
63 size_t d = src.getMat(0).total();
65 Mat data((int)n, (int)d, rtype);
67 for(unsigned int i = 0; i < n; i++) {
68 // make sure data can be reshaped, throw exception if not!
69 if(src.getMat(i).total() != d) {
70 String error_message = format("Wrong number of elements in matrix #%d! Expected %d was %d.", i, d, src.getMat(i).total());
71 CV_Error(Error::StsBadArg, error_message);
73 // get a hold of the current row
75 // make reshape happy by cloning for non-continuous matrices
76 if(src.getMat(i).isContinuous()) {
77 src.getMat(i).reshape(1, 1).convertTo(xi, rtype, alpha, beta);
79 src.getMat(i).clone().reshape(1, 1).convertTo(xi, rtype, alpha, beta);
86 // Removes duplicate elements in a given vector.
87 template<typename _Tp>
88 inline std::vector<_Tp> remove_dups(const std::vector<_Tp>& src) {
89 typedef typename std::set<_Tp>::const_iterator constSetIterator;
90 typedef typename std::vector<_Tp>::const_iterator constVecIterator;
91 std::set<_Tp> set_elems;
92 for (constVecIterator it = src.begin(); it != src.end(); ++it)
93 set_elems.insert(*it);
94 std::vector<_Tp> elems;
95 for (constSetIterator it = set_elems.begin(); it != set_elems.end(); ++it)
101 // Turk, M., and Pentland, A. "Eigenfaces for recognition.". Journal of
102 // Cognitive Neuroscience 3 (1991), 71–86.
103 class Eigenfaces : public FaceRecognizer
108 std::vector<Mat> _projections;
115 using FaceRecognizer::save;
116 using FaceRecognizer::load;
118 // Initializes an empty Eigenfaces model.
119 Eigenfaces(int num_components = 0, double threshold = DBL_MAX) :
120 _num_components(num_components),
121 _threshold(threshold) {}
123 // Initializes and computes an Eigenfaces model with images in src and
124 // corresponding labels in labels. num_components will be kept for
126 Eigenfaces(InputArrayOfArrays src, InputArray labels,
127 int num_components = 0, double threshold = DBL_MAX) :
128 _num_components(num_components),
129 _threshold(threshold) {
133 // Computes an Eigenfaces model with images in src and corresponding labels
135 void train(InputArrayOfArrays src, InputArray labels);
137 // Predicts the label of a query image in src.
138 int predict(InputArray src) const;
140 // Predicts the label and confidence for a given sample.
141 void predict(InputArray _src, int &label, double &dist) const;
143 // See FaceRecognizer::load.
144 void load(const FileStorage& fs);
146 // See FaceRecognizer::save.
147 void save(FileStorage& fs) const;
149 AlgorithmInfo* info() const;
152 // Belhumeur, P. N., Hespanha, J., and Kriegman, D. "Eigenfaces vs. Fisher-
153 // faces: Recognition using class specific linear projection.". IEEE
154 // Transactions on Pattern Analysis and Machine Intelligence 19, 7 (1997),
156 class Fisherfaces: public FaceRecognizer
164 std::vector<Mat> _projections;
168 using FaceRecognizer::save;
169 using FaceRecognizer::load;
171 // Initializes an empty Fisherfaces model.
172 Fisherfaces(int num_components = 0, double threshold = DBL_MAX) :
173 _num_components(num_components),
174 _threshold(threshold) {}
176 // Initializes and computes a Fisherfaces model with images in src and
177 // corresponding labels in labels. num_components will be kept for
179 Fisherfaces(InputArrayOfArrays src, InputArray labels,
180 int num_components = 0, double threshold = DBL_MAX) :
181 _num_components(num_components),
182 _threshold(threshold) {
188 // Computes a Fisherfaces model with images in src and corresponding labels
190 void train(InputArrayOfArrays src, InputArray labels);
192 // Predicts the label of a query image in src.
193 int predict(InputArray src) const;
195 // Predicts the label and confidence for a given sample.
196 void predict(InputArray _src, int &label, double &dist) const;
198 // See FaceRecognizer::load.
199 void load(const FileStorage& fs);
201 // See FaceRecognizer::save.
202 void save(FileStorage& fs) const;
204 AlgorithmInfo* info() const;
207 // Face Recognition based on Local Binary Patterns.
209 // Ahonen T, Hadid A. and Pietikäinen M. "Face description with local binary
210 // patterns: Application to face recognition." IEEE Transactions on Pattern
211 // Analysis and Machine Intelligence, 28(12):2037-2041.
213 class LBPH : public FaceRecognizer
222 std::vector<Mat> _histograms;
225 // Computes a LBPH model with images in src and
226 // corresponding labels in labels, possibly preserving
228 void train(InputArrayOfArrays src, InputArray labels, bool preserveData);
232 using FaceRecognizer::save;
233 using FaceRecognizer::load;
235 // Initializes this LBPH Model. The current implementation is rather fixed
236 // as it uses the Extended Local Binary Patterns per default.
238 // radius, neighbors are used in the local binary patterns creation.
239 // grid_x, grid_y control the grid size of the spatial histograms.
240 LBPH(int radius_=1, int neighbors_=8,
241 int gridx=8, int gridy=8,
242 double threshold = DBL_MAX) :
246 _neighbors(neighbors_),
247 _threshold(threshold) {}
249 // Initializes and computes this LBPH Model. The current implementation is
250 // rather fixed as it uses the Extended Local Binary Patterns per default.
252 // (radius=1), (neighbors=8) are used in the local binary patterns creation.
253 // (grid_x=8), (grid_y=8) controls the grid size of the spatial histograms.
254 LBPH(InputArrayOfArrays src,
256 int radius_=1, int neighbors_=8,
257 int gridx=8, int gridy=8,
258 double threshold = DBL_MAX) :
262 _neighbors(neighbors_),
263 _threshold(threshold) {
269 // Computes a LBPH model with images in src and
270 // corresponding labels in labels.
271 void train(InputArrayOfArrays src, InputArray labels);
273 // Updates this LBPH model with images in src and
274 // corresponding labels in labels.
275 void update(InputArrayOfArrays src, InputArray labels);
277 // Predicts the label of a query image in src.
278 int predict(InputArray src) const;
280 // Predicts the label and confidence for a given sample.
281 void predict(InputArray _src, int &label, double &dist) const;
283 // See FaceRecognizer::load.
284 void load(const FileStorage& fs);
286 // See FaceRecognizer::save.
287 void save(FileStorage& fs) const;
290 int neighbors() const { return _neighbors; }
291 int radius() const { return _radius; }
292 int grid_x() const { return _grid_x; }
293 int grid_y() const { return _grid_y; }
295 AlgorithmInfo* info() const;
299 //------------------------------------------------------------------------------
301 //------------------------------------------------------------------------------
302 void FaceRecognizer::update(InputArrayOfArrays src, InputArray labels ) {
303 if( dynamic_cast<LBPH*>(this) != 0 )
305 dynamic_cast<LBPH*>(this)->update( src, labels );
309 String error_msg = format("This FaceRecognizer (%s) does not support updating, you have to use FaceRecognizer::train to update it.", this->name().c_str());
310 CV_Error(Error::StsNotImplemented, error_msg);
313 void FaceRecognizer::save(const String& filename) const {
314 FileStorage fs(filename, FileStorage::WRITE);
316 CV_Error(Error::StsError, "File can't be opened for writing!");
321 void FaceRecognizer::load(const String& filename) {
322 FileStorage fs(filename, FileStorage::READ);
324 CV_Error(Error::StsError, "File can't be opened for writing!");
329 //------------------------------------------------------------------------------
331 //------------------------------------------------------------------------------
332 void Eigenfaces::train(InputArrayOfArrays _src, InputArray _local_labels) {
333 if(_src.total() == 0) {
334 String error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
335 CV_Error(Error::StsBadArg, error_message);
336 } else if(_local_labels.getMat().type() != CV_32SC1) {
337 String error_message = format("Labels must be given as integer (CV_32SC1). Expected %d, but was %d.", CV_32SC1, _local_labels.type());
338 CV_Error(Error::StsBadArg, error_message);
340 // make sure data has correct size
341 if(_src.total() > 1) {
342 for(int i = 1; i < static_cast<int>(_src.total()); i++) {
343 if(_src.getMat(i-1).total() != _src.getMat(i).total()) {
344 String error_message = format("In the Eigenfaces method all input samples (training images) must be of equal size! Expected %d pixels, but was %d pixels.", _src.getMat(i-1).total(), _src.getMat(i).total());
345 CV_Error(Error::StsUnsupportedFormat, error_message);
350 Mat labels = _local_labels.getMat();
351 // observations in row
352 Mat data = asRowMatrix(_src, CV_64FC1);
356 // assert there are as much samples as labels
357 if(static_cast<int>(labels.total()) != n) {
358 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());
359 CV_Error(Error::StsBadArg, error_message);
361 // clear existing model data
363 _projections.clear();
364 // clip number of components to be valid
365 if((_num_components <= 0) || (_num_components > n))
369 PCA pca(data, Mat(), PCA::DATA_AS_ROW, _num_components);
370 // copy the PCA results
371 _mean = pca.mean.reshape(1,1); // store the mean vector
372 _eigenvalues = pca.eigenvalues.clone(); // eigenvalues by row
373 transpose(pca.eigenvectors, _eigenvectors); // eigenvectors by column
374 // store labels for prediction
375 _labels = labels.clone();
377 for(int sampleIdx = 0; sampleIdx < data.rows; sampleIdx++) {
378 Mat p = subspaceProject(_eigenvectors, _mean, data.row(sampleIdx));
379 _projections.push_back(p);
383 void Eigenfaces::predict(InputArray _src, int &minClass, double &minDist) const {
385 Mat src = _src.getMat();
386 // make sure the user is passing correct data
387 if(_projections.empty()) {
388 // throw error if no data (or simply return -1?)
389 String error_message = "This Eigenfaces model is not computed yet. Did you call Eigenfaces::train?";
390 CV_Error(Error::StsError, error_message);
391 } else if(_eigenvectors.rows != static_cast<int>(src.total())) {
392 // check data alignment just for clearer exception messages
393 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());
394 CV_Error(Error::StsBadArg, error_message);
396 // project into PCA subspace
397 Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1));
400 for(size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) {
401 double dist = norm(_projections[sampleIdx], q, NORM_L2);
402 if((dist < minDist) && (dist < _threshold)) {
404 minClass = _labels.at<int>((int)sampleIdx);
409 int Eigenfaces::predict(InputArray _src) const {
412 predict(_src, label, dummy);
416 void Eigenfaces::load(const FileStorage& fs) {
418 fs["num_components"] >> _num_components;
420 fs["eigenvalues"] >> _eigenvalues;
421 fs["eigenvectors"] >> _eigenvectors;
423 readFileNodeList(fs["projections"], _projections);
424 fs["labels"] >> _labels;
427 void Eigenfaces::save(FileStorage& fs) const {
429 fs << "num_components" << _num_components;
430 fs << "mean" << _mean;
431 fs << "eigenvalues" << _eigenvalues;
432 fs << "eigenvectors" << _eigenvectors;
434 writeFileNodeList(fs, "projections", _projections);
435 fs << "labels" << _labels;
438 //------------------------------------------------------------------------------
440 //------------------------------------------------------------------------------
441 void Fisherfaces::train(InputArrayOfArrays src, InputArray _lbls) {
442 if(src.total() == 0) {
443 String error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
444 CV_Error(Error::StsBadArg, error_message);
445 } else if(_lbls.getMat().type() != CV_32SC1) {
446 String error_message = format("Labels must be given as integer (CV_32SC1). Expected %d, but was %d.", CV_32SC1, _lbls.type());
447 CV_Error(Error::StsBadArg, error_message);
449 // make sure data has correct size
450 if(src.total() > 1) {
451 for(int i = 1; i < static_cast<int>(src.total()); i++) {
452 if(src.getMat(i-1).total() != src.getMat(i).total()) {
453 String error_message = format("In the Fisherfaces method all input samples (training images) must be of equal size! Expected %d pixels, but was %d pixels.", src.getMat(i-1).total(), src.getMat(i).total());
454 CV_Error(Error::StsUnsupportedFormat, error_message);
459 Mat labels = _lbls.getMat();
460 Mat data = asRowMatrix(src, CV_64FC1);
463 // make sure labels are passed in correct shape
464 if(labels.total() != (size_t) N) {
465 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());
466 CV_Error(Error::StsBadArg, error_message);
467 } else if(labels.rows != 1 && labels.cols != 1) {
468 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);
469 CV_Error(Error::StsBadArg, error_message);
471 // clear existing model data
473 _projections.clear();
474 // safely copy from cv::Mat to std::vector
476 for(unsigned int i = 0; i < labels.total(); i++) {
477 ll.push_back(labels.at<int>(i));
479 // get the number of unique classes
480 int C = (int) remove_dups(ll).size();
481 // clip number of components to be a valid number
482 if((_num_components <= 0) || (_num_components > (C-1)))
483 _num_components = (C-1);
484 // perform a PCA and keep (N-C) components
485 PCA pca(data, Mat(), PCA::DATA_AS_ROW, (N-C));
486 // project the data and perform a LDA on it
487 LDA lda(pca.project(data),labels, _num_components);
488 // store the total mean vector
489 _mean = pca.mean.reshape(1,1);
491 _labels = labels.clone();
492 // store the eigenvalues of the discriminants
493 lda.eigenvalues().convertTo(_eigenvalues, CV_64FC1);
494 // Now calculate the projection matrix as pca.eigenvectors * lda.eigenvectors.
495 // Note: OpenCV stores the eigenvectors by row, so we need to transpose it!
496 gemm(pca.eigenvectors, lda.eigenvectors(), 1.0, Mat(), 0.0, _eigenvectors, GEMM_1_T);
497 // store the projections of the original data
498 for(int sampleIdx = 0; sampleIdx < data.rows; sampleIdx++) {
499 Mat p = subspaceProject(_eigenvectors, _mean, data.row(sampleIdx));
500 _projections.push_back(p);
504 void Fisherfaces::predict(InputArray _src, int &minClass, double &minDist) const {
505 Mat src = _src.getMat();
506 // check data alignment just for clearer exception messages
507 if(_projections.empty()) {
508 // throw error if no data (or simply return -1?)
509 String error_message = "This Fisherfaces model is not computed yet. Did you call Fisherfaces::train?";
510 CV_Error(Error::StsBadArg, error_message);
511 } else if(src.total() != (size_t) _eigenvectors.rows) {
512 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());
513 CV_Error(Error::StsBadArg, error_message);
515 // project into LDA subspace
516 Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1));
517 // find 1-nearest neighbor
520 for(size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) {
521 double dist = norm(_projections[sampleIdx], q, NORM_L2);
522 if((dist < minDist) && (dist < _threshold)) {
524 minClass = _labels.at<int>((int)sampleIdx);
529 int Fisherfaces::predict(InputArray _src) const {
532 predict(_src, label, dummy);
536 // See FaceRecognizer::load.
537 void Fisherfaces::load(const FileStorage& fs) {
539 fs["num_components"] >> _num_components;
541 fs["eigenvalues"] >> _eigenvalues;
542 fs["eigenvectors"] >> _eigenvectors;
544 readFileNodeList(fs["projections"], _projections);
545 fs["labels"] >> _labels;
548 // See FaceRecognizer::save.
549 void Fisherfaces::save(FileStorage& fs) const {
551 fs << "num_components" << _num_components;
552 fs << "mean" << _mean;
553 fs << "eigenvalues" << _eigenvalues;
554 fs << "eigenvectors" << _eigenvectors;
556 writeFileNodeList(fs, "projections", _projections);
557 fs << "labels" << _labels;
560 //------------------------------------------------------------------------------
562 //------------------------------------------------------------------------------
564 template <typename _Tp> static
565 void olbp_(InputArray _src, OutputArray _dst) {
567 Mat src = _src.getMat();
568 // allocate memory for result
569 _dst.create(src.rows-2, src.cols-2, CV_8UC1);
570 Mat dst = _dst.getMat();
571 // zero the result matrix
573 // calculate patterns
574 for(int i=1;i<src.rows-1;i++) {
575 for(int j=1;j<src.cols-1;j++) {
576 _Tp center = src.at<_Tp>(i,j);
577 unsigned char code = 0;
578 code |= (src.at<_Tp>(i-1,j-1) >= center) << 7;
579 code |= (src.at<_Tp>(i-1,j) >= center) << 6;
580 code |= (src.at<_Tp>(i-1,j+1) >= center) << 5;
581 code |= (src.at<_Tp>(i,j+1) >= center) << 4;
582 code |= (src.at<_Tp>(i+1,j+1) >= center) << 3;
583 code |= (src.at<_Tp>(i+1,j) >= center) << 2;
584 code |= (src.at<_Tp>(i+1,j-1) >= center) << 1;
585 code |= (src.at<_Tp>(i,j-1) >= center) << 0;
586 dst.at<unsigned char>(i-1,j-1) = code;
591 //------------------------------------------------------------------------------
593 //------------------------------------------------------------------------------
594 template <typename _Tp> static
595 inline void elbp_(InputArray _src, OutputArray _dst, int radius, int neighbors) {
597 Mat src = _src.getMat();
598 // allocate memory for result
599 _dst.create(src.rows-2*radius, src.cols-2*radius, CV_32SC1);
600 Mat dst = _dst.getMat();
603 for(int n=0; n<neighbors; n++) {
605 float x = static_cast<float>(radius * cos(2.0*CV_PI*n/static_cast<float>(neighbors)));
606 float y = static_cast<float>(-radius * sin(2.0*CV_PI*n/static_cast<float>(neighbors)));
608 int fx = static_cast<int>(floor(x));
609 int fy = static_cast<int>(floor(y));
610 int cx = static_cast<int>(ceil(x));
611 int cy = static_cast<int>(ceil(y));
615 // set interpolation weights
616 float w1 = (1 - tx) * (1 - ty);
617 float w2 = tx * (1 - ty);
618 float w3 = (1 - tx) * ty;
620 // iterate through your data
621 for(int i=radius; i < src.rows-radius;i++) {
622 for(int j=radius;j < src.cols-radius;j++) {
623 // calculate interpolated value
624 float t = static_cast<float>(w1*src.at<_Tp>(i+fy,j+fx) + w2*src.at<_Tp>(i+fy,j+cx) + w3*src.at<_Tp>(i+cy,j+fx) + w4*src.at<_Tp>(i+cy,j+cx));
625 // floating point precision, so check some machine-dependent epsilon
626 dst.at<int>(i-radius,j-radius) += ((t > src.at<_Tp>(i,j)) || (std::abs(t-src.at<_Tp>(i,j)) < std::numeric_limits<float>::epsilon())) << n;
632 static void elbp(InputArray src, OutputArray dst, int radius, int neighbors)
634 int type = src.type();
636 case CV_8SC1: elbp_<char>(src,dst, radius, neighbors); break;
637 case CV_8UC1: elbp_<unsigned char>(src, dst, radius, neighbors); break;
638 case CV_16SC1: elbp_<short>(src,dst, radius, neighbors); break;
639 case CV_16UC1: elbp_<unsigned short>(src,dst, radius, neighbors); break;
640 case CV_32SC1: elbp_<int>(src,dst, radius, neighbors); break;
641 case CV_32FC1: elbp_<float>(src,dst, radius, neighbors); break;
642 case CV_64FC1: elbp_<double>(src,dst, radius, neighbors); break;
644 String error_msg = format("Using Original Local Binary Patterns for feature extraction only works on single-channel images (given %d). Please pass the image data as a grayscale image!", type);
645 CV_Error(Error::StsNotImplemented, error_msg);
651 histc_(const Mat& src, int minVal=0, int maxVal=255, bool normed=false)
654 // Establish the number of bins.
655 int histSize = maxVal-minVal+1;
657 float range[] = { static_cast<float>(minVal), static_cast<float>(maxVal+1) };
658 const float* histRange = { range };
660 calcHist(&src, 1, 0, Mat(), result, 1, &histSize, &histRange, true, false);
663 result /= (int)src.total();
665 return result.reshape(1,1);
668 static Mat histc(InputArray _src, int minVal, int maxVal, bool normed)
670 Mat src = _src.getMat();
671 switch (src.type()) {
673 return histc_(Mat_<float>(src), minVal, maxVal, normed);
676 return histc_(src, minVal, maxVal, normed);
679 return histc_(Mat_<float>(src), minVal, maxVal, normed);
682 return histc_(src, minVal, maxVal, normed);
685 return histc_(Mat_<float>(src), minVal, maxVal, normed);
688 return histc_(src, minVal, maxVal, normed);
691 CV_Error(Error::StsUnmatchedFormats, "This type is not implemented yet."); break;
697 static Mat spatial_histogram(InputArray _src, int numPatterns,
698 int grid_x, int grid_y, bool /*normed*/)
700 Mat src = _src.getMat();
701 // calculate LBP patch size
702 int width = src.cols/grid_x;
703 int height = src.rows/grid_y;
704 // allocate memory for the spatial histogram
705 Mat result = Mat::zeros(grid_x * grid_y, numPatterns, CV_32FC1);
706 // return matrix with zeros if no data was given
708 return result.reshape(1,1);
709 // initial result_row
710 int resultRowIdx = 0;
711 // iterate through grid
712 for(int i = 0; i < grid_y; i++) {
713 for(int j = 0; j < grid_x; j++) {
714 Mat src_cell = Mat(src, Range(i*height,(i+1)*height), Range(j*width,(j+1)*width));
715 Mat cell_hist = histc(src_cell, 0, (numPatterns-1), true);
716 // copy to the result matrix
717 Mat result_row = result.row(resultRowIdx);
718 cell_hist.reshape(1,1).convertTo(result_row, CV_32FC1);
719 // increase row count in result matrix
723 // return result as reshaped feature vector
724 return result.reshape(1,1);
727 //------------------------------------------------------------------------------
728 // wrapper to cv::elbp (extended local binary patterns)
729 //------------------------------------------------------------------------------
731 static Mat elbp(InputArray src, int radius, int neighbors) {
733 elbp(src, dst, radius, neighbors);
737 void LBPH::load(const FileStorage& fs) {
738 fs["radius"] >> _radius;
739 fs["neighbors"] >> _neighbors;
740 fs["grid_x"] >> _grid_x;
741 fs["grid_y"] >> _grid_y;
743 readFileNodeList(fs["histograms"], _histograms);
744 fs["labels"] >> _labels;
747 // See FaceRecognizer::save.
748 void LBPH::save(FileStorage& fs) const {
749 fs << "radius" << _radius;
750 fs << "neighbors" << _neighbors;
751 fs << "grid_x" << _grid_x;
752 fs << "grid_y" << _grid_y;
754 writeFileNodeList(fs, "histograms", _histograms);
755 fs << "labels" << _labels;
758 void LBPH::train(InputArrayOfArrays _in_src, InputArray _in_labels) {
759 this->train(_in_src, _in_labels, false);
762 void LBPH::update(InputArrayOfArrays _in_src, InputArray _in_labels) {
763 // got no data, just return
764 if(_in_src.total() == 0)
767 this->train(_in_src, _in_labels, true);
770 void LBPH::train(InputArrayOfArrays _in_src, InputArray _in_labels, bool preserveData) {
771 if(_in_src.kind() != _InputArray::STD_VECTOR_MAT && _in_src.kind() != _InputArray::STD_VECTOR_VECTOR) {
772 String error_message = "The images are expected as InputArray::STD_VECTOR_MAT (a std::vector<Mat>) or _InputArray::STD_VECTOR_VECTOR (a std::vector< std::vector<...> >).";
773 CV_Error(Error::StsBadArg, error_message);
775 if(_in_src.total() == 0) {
776 String error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
777 CV_Error(Error::StsUnsupportedFormat, error_message);
778 } else if(_in_labels.getMat().type() != CV_32SC1) {
779 String error_message = format("Labels must be given as integer (CV_32SC1). Expected %d, but was %d.", CV_32SC1, _in_labels.type());
780 CV_Error(Error::StsUnsupportedFormat, error_message);
782 // get the vector of matrices
783 std::vector<Mat> src;
784 _in_src.getMatVector(src);
785 // get the label matrix
786 Mat labels = _in_labels.getMat();
787 // check if data is well- aligned
788 if(labels.total() != src.size()) {
789 String error_message = format("The number of samples (src) must equal the number of labels (labels). Was len(samples)=%d, len(labels)=%d.", src.size(), _labels.total());
790 CV_Error(Error::StsBadArg, error_message);
792 // if this model should be trained without preserving old data, delete old model data
797 // append labels to _labels matrix
798 for(size_t labelIdx = 0; labelIdx < labels.total(); labelIdx++) {
799 _labels.push_back(labels.at<int>((int)labelIdx));
801 // store the spatial histograms of the original data
802 for(size_t sampleIdx = 0; sampleIdx < src.size(); sampleIdx++) {
803 // calculate lbp image
804 Mat lbp_image = elbp(src[sampleIdx], _radius, _neighbors);
805 // get spatial histogram from this lbp image
806 Mat p = spatial_histogram(
807 lbp_image, /* lbp_image */
808 static_cast<int>(std::pow(2.0, static_cast<double>(_neighbors))), /* number of possible patterns */
809 _grid_x, /* grid size x */
810 _grid_y, /* grid size y */
813 _histograms.push_back(p);
817 void LBPH::predict(InputArray _src, int &minClass, double &minDist) const {
818 if(_histograms.empty()) {
819 // throw error if no data (or simply return -1?)
820 String error_message = "This LBPH model is not computed yet. Did you call the train method?";
821 CV_Error(Error::StsBadArg, error_message);
823 Mat src = _src.getMat();
824 // get the spatial histogram from input image
825 Mat lbp_image = elbp(src, _radius, _neighbors);
826 Mat query = spatial_histogram(
827 lbp_image, /* lbp_image */
828 static_cast<int>(std::pow(2.0, static_cast<double>(_neighbors))), /* number of possible patterns */
829 _grid_x, /* grid size x */
830 _grid_y, /* grid size y */
831 true /* normed histograms */);
832 // find 1-nearest neighbor
835 for(size_t sampleIdx = 0; sampleIdx < _histograms.size(); sampleIdx++) {
836 double dist = compareHist(_histograms[sampleIdx], query, HISTCMP_CHISQR_ALT);
837 if((dist < minDist) && (dist < _threshold)) {
839 minClass = _labels.at<int>((int) sampleIdx);
844 int LBPH::predict(InputArray _src) const {
847 predict(_src, label, dummy);
852 Ptr<FaceRecognizer> createEigenFaceRecognizer(int num_components, double threshold)
854 return makePtr<Eigenfaces>(num_components, threshold);
857 Ptr<FaceRecognizer> createFisherFaceRecognizer(int num_components, double threshold)
859 return makePtr<Fisherfaces>(num_components, threshold);
862 Ptr<FaceRecognizer> createLBPHFaceRecognizer(int radius, int neighbors,
863 int grid_x, int grid_y, double threshold)
865 return makePtr<LBPH>(radius, neighbors, grid_x, grid_y, threshold);
868 CV_INIT_ALGORITHM(Eigenfaces, "FaceRecognizer.Eigenfaces",
869 obj.info()->addParam(obj, "ncomponents", obj._num_components);
870 obj.info()->addParam(obj, "threshold", obj._threshold);
871 obj.info()->addParam(obj, "projections", obj._projections, true);
872 obj.info()->addParam(obj, "labels", obj._labels, true);
873 obj.info()->addParam(obj, "eigenvectors", obj._eigenvectors, true);
874 obj.info()->addParam(obj, "eigenvalues", obj._eigenvalues, true);
875 obj.info()->addParam(obj, "mean", obj._mean, true))
877 CV_INIT_ALGORITHM(Fisherfaces, "FaceRecognizer.Fisherfaces",
878 obj.info()->addParam(obj, "ncomponents", obj._num_components);
879 obj.info()->addParam(obj, "threshold", obj._threshold);
880 obj.info()->addParam(obj, "projections", obj._projections, true);
881 obj.info()->addParam(obj, "labels", obj._labels, true);
882 obj.info()->addParam(obj, "eigenvectors", obj._eigenvectors, true);
883 obj.info()->addParam(obj, "eigenvalues", obj._eigenvalues, true);
884 obj.info()->addParam(obj, "mean", obj._mean, true))
886 CV_INIT_ALGORITHM(LBPH, "FaceRecognizer.LBPH",
887 obj.info()->addParam(obj, "radius", obj._radius);
888 obj.info()->addParam(obj, "neighbors", obj._neighbors);
889 obj.info()->addParam(obj, "grid_x", obj._grid_x);
890 obj.info()->addParam(obj, "grid_y", obj._grid_y);
891 obj.info()->addParam(obj, "threshold", obj._threshold);
892 obj.info()->addParam(obj, "histograms", obj._histograms, true);
893 obj.info()->addParam(obj, "labels", obj._labels, true))
895 bool initModule_contrib()
897 Ptr<Algorithm> efaces = createEigenfaces_ptr_hidden(), ffaces = createFisherfaces_ptr_hidden(), lbph = createLBPH_ptr_hidden();
898 return efaces->info() != 0 && ffaces->info() != 0 && lbph->info() != 0;