Renesas GR-PEACH OpenCV Development / gr-peach-opencv-project-sd-card_update

Fork of gr-peach-opencv-project-sd-card by the do

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers fisher_faces.cpp Source File

fisher_faces.cpp

00001 /*
00002  * Copyright (c) 2011,2012. Philipp Wagner <bytefish[at]gmx[dot]de>.
00003  * Released to public domain under terms of the BSD Simplified license.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *   * Redistributions of source code must retain the above copyright
00008  *     notice, this list of conditions and the following disclaimer.
00009  *   * Redistributions in binary form must reproduce the above copyright
00010  *     notice, this list of conditions and the following disclaimer in the
00011  *     documentation and/or other materials provided with the distribution.
00012  *   * Neither the name of the organization nor the names of its contributors
00013  *     may be used to endorse or promote products derived from this software
00014  *     without specific prior written permission.
00015  *
00016  *   See <http://www.opensource.org/licenses/bsd-license>
00017  */
00018 #include "precomp.hpp"
00019 #include "face_basic.hpp"
00020 
00021 namespace cv { namespace face {
00022 
00023 // Belhumeur, P. N., Hespanha, J., and Kriegman, D. "Eigenfaces vs. Fisher-
00024 // faces: Recognition using class specific linear projection.". IEEE
00025 // Transactions on Pattern Analysis and Machine Intelligence 19, 7 (1997),
00026 // 711–720.
00027 class Fisherfaces: public BasicFaceRecognizerImpl
00028 {
00029 public:
00030     // Initializes an empty Fisherfaces model.
00031     Fisherfaces(int num_components = 0, double threshold = DBL_MAX)
00032         : BasicFaceRecognizerImpl(num_components, threshold)
00033     { }
00034 
00035     // Computes a Fisherfaces model with images in src and corresponding labels
00036     // in labels.
00037     void train(InputArrayOfArrays src, InputArray labels);
00038 
00039     // Send all predict results to caller side for custom result handling
00040     void predict(InputArray src, Ptr<PredictCollector> collector) const;
00041 };
00042 
00043 // Removes duplicate elements in a given vector.
00044 template<typename _Tp>
00045 inline std::vector<_Tp> remove_dups(const std::vector<_Tp>& src) {
00046     typedef typename std::set<_Tp>::const_iterator constSetIterator;
00047     typedef typename std::vector<_Tp>::const_iterator constVecIterator;
00048     std::set<_Tp> set_elems;
00049     for (constVecIterator it = src.begin(); it != src.end(); ++it)
00050         set_elems.insert(*it);
00051     std::vector<_Tp> elems;
00052     for (constSetIterator it = set_elems.begin(); it != set_elems.end(); ++it)
00053         elems.push_back(*it);
00054     return elems;
00055 }
00056 
00057 //------------------------------------------------------------------------------
00058 // Fisherfaces
00059 //------------------------------------------------------------------------------
00060 void Fisherfaces::train(InputArrayOfArrays src, InputArray _lbls) {
00061     if(src.total() == 0) {
00062         String error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
00063         CV_Error(Error::StsBadArg, error_message);
00064     } else if(_lbls.getMat().type() != CV_32SC1) {
00065         String error_message = format("Labels must be given as integer (CV_32SC1). Expected %d, but was %d.", CV_32SC1, _lbls.type());
00066         CV_Error(Error::StsBadArg, error_message);
00067     }
00068     // make sure data has correct size
00069     if(src.total() > 1) {
00070         for(int i = 1; i < static_cast<int>(src.total()); i++) {
00071             if(src.getMat(i-1).total() != src.getMat(i).total()) {
00072                 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());
00073                 CV_Error(Error::StsUnsupportedFormat, error_message);
00074             }
00075         }
00076     }
00077     // get data
00078     Mat labels = _lbls.getMat();
00079     Mat data = asRowMatrix(src, CV_64FC1);
00080     // number of samples
00081     int N = data.rows;
00082     // make sure labels are passed in correct shape
00083     if(labels.total() != (size_t) N) {
00084         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());
00085         CV_Error(Error::StsBadArg, error_message);
00086     } else if(labels.rows != 1 && labels.cols != 1) {
00087         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);
00088        CV_Error(Error::StsBadArg, error_message);
00089     }
00090     // clear existing model data
00091     _labels.release();
00092     _projections.clear();
00093     // safely copy from cv::Mat to std::vector
00094     std::vector<int> ll;
00095     for(unsigned int i = 0; i < labels.total(); i++) {
00096         ll.push_back(labels.at<int>(i));
00097     }
00098     // get the number of unique classes
00099     int C = (int) remove_dups(ll).size();
00100     // clip number of components to be a valid number
00101     if((_num_components <= 0) || (_num_components > (C-1)))
00102         _num_components = (C-1);
00103     // perform a PCA and keep (N-C) components
00104     PCA pca(data, Mat(), PCA::DATA_AS_ROW, (N-C));
00105     // project the data and perform a LDA on it
00106     LDA lda(pca.project(data),labels, _num_components);
00107     // store the total mean vector
00108     _mean = pca.mean.reshape(1,1);
00109     // store labels
00110     _labels = labels.clone();
00111     // store the eigenvalues of the discriminants
00112     lda.eigenvalues().convertTo(_eigenvalues, CV_64FC1);
00113     // Now calculate the projection matrix as pca.eigenvectors * lda.eigenvectors.
00114     // Note: OpenCV stores the eigenvectors by row, so we need to transpose it!
00115     gemm(pca.eigenvectors, lda.eigenvectors(), 1.0, Mat(), 0.0, _eigenvectors, GEMM_1_T);
00116     // store the projections of the original data
00117     for(int sampleIdx = 0; sampleIdx < data.rows; sampleIdx++) {
00118         Mat p = LDA::subspaceProject(_eigenvectors, _mean, data.row(sampleIdx));
00119         _projections.push_back(p);
00120     }
00121 }
00122 
00123 void Fisherfaces::predict(InputArray _src, Ptr<PredictCollector> collector) const {
00124     Mat src = _src.getMat();
00125     // check data alignment just for clearer exception messages
00126     if(_projections.empty()) {
00127         // throw error if no data (or simply return -1?)
00128         String error_message = "This Fisherfaces model is not computed yet. Did you call Fisherfaces::train?";
00129         CV_Error(Error::StsBadArg, error_message);
00130     } else if(src.total() != (size_t) _eigenvectors.rows) {
00131         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());
00132         CV_Error(Error::StsBadArg, error_message);
00133     }
00134     // project into LDA subspace
00135     Mat q = LDA::subspaceProject(_eigenvectors, _mean, src.reshape(1,1));
00136     // find 1-nearest neighbor
00137     collector->init((int)_projections.size());
00138     for (size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) {
00139         double dist = norm(_projections[sampleIdx], q, NORM_L2);
00140         int label = _labels.at<int>((int)sampleIdx);
00141         if (!collector->collect(label, dist))return;
00142     }
00143 }
00144 
00145 Ptr<BasicFaceRecognizer> createFisherFaceRecognizer (int num_components, double threshold)
00146 {
00147     return makePtr<Fisherfaces>(num_components, threshold);
00148 }
00149 
00150 } }
00151