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 lbph_faces.cpp Source File

lbph_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 "opencv2/face.hpp"
00020 #include "face_basic.hpp"
00021 
00022 namespace cv { namespace face {
00023 
00024 // Face Recognition based on Local Binary Patterns.
00025 //
00026 //  Ahonen T, Hadid A. and Pietikäinen M. "Face description with local binary
00027 //  patterns: Application to face recognition." IEEE Transactions on Pattern
00028 //  Analysis and Machine Intelligence, 28(12):2037-2041.
00029 //
00030 class LBPH : public LBPHFaceRecognizer
00031 {
00032 private:
00033     int _grid_x;
00034     int _grid_y;
00035     int _radius;
00036     int _neighbors;
00037     double _threshold;
00038 
00039     std::vector<Mat> _histograms;
00040     Mat _labels;
00041 
00042     // Computes a LBPH model with images in src and
00043     // corresponding labels in labels, possibly preserving
00044     // old model data.
00045     void train(InputArrayOfArrays src, InputArray labels, bool preserveData);
00046 
00047 
00048 public:
00049     using FaceRecognizer::save;
00050     using FaceRecognizer::load;
00051 
00052     // Initializes this LBPH Model. The current implementation is rather fixed
00053     // as it uses the Extended Local Binary Patterns per default.
00054     //
00055     // radius, neighbors are used in the local binary patterns creation.
00056     // grid_x, grid_y control the grid size of the spatial histograms.
00057     LBPH(int radius_=1, int neighbors_=8,
00058             int gridx=8, int gridy=8,
00059             double threshold = DBL_MAX) :
00060         _grid_x(gridx),
00061         _grid_y(gridy),
00062         _radius(radius_),
00063         _neighbors(neighbors_),
00064         _threshold(threshold) {}
00065 
00066     // Initializes and computes this LBPH Model. The current implementation is
00067     // rather fixed as it uses the Extended Local Binary Patterns per default.
00068     //
00069     // (radius=1), (neighbors=8) are used in the local binary patterns creation.
00070     // (grid_x=8), (grid_y=8) controls the grid size of the spatial histograms.
00071     LBPH(InputArrayOfArrays src,
00072             InputArray labels,
00073             int radius_=1, int neighbors_=8,
00074             int gridx=8, int gridy=8,
00075             double threshold = DBL_MAX) :
00076                 _grid_x(gridx),
00077                 _grid_y(gridy),
00078                 _radius(radius_),
00079                 _neighbors(neighbors_),
00080                 _threshold(threshold) {
00081         train(src, labels);
00082     }
00083 
00084     ~LBPH() { }
00085 
00086     // Computes a LBPH model with images in src and
00087     // corresponding labels in labels.
00088     void train(InputArrayOfArrays src, InputArray labels);
00089 
00090     // Updates this LBPH model with images in src and
00091     // corresponding labels in labels.
00092     void update(InputArrayOfArrays src, InputArray labels);
00093 
00094     // Send all predict results to caller side for custom result handling
00095     void predict(InputArray src, Ptr<PredictCollector> collector) const;
00096 
00097     // See FaceRecognizer::load.
00098     void load(const FileStorage& fs);
00099 
00100     // See FaceRecognizer::save.
00101     void save(FileStorage& fs) const;
00102 
00103     CV_IMPL_PROPERTY(int, GridX, _grid_x)
00104     CV_IMPL_PROPERTY(int, GridY, _grid_y)
00105     CV_IMPL_PROPERTY(int, Radius, _radius)
00106     CV_IMPL_PROPERTY(int, Neighbors, _neighbors)
00107     CV_IMPL_PROPERTY(double, Threshold, _threshold)
00108     CV_IMPL_PROPERTY_RO(std::vector<cv::Mat>, Histograms, _histograms)
00109     CV_IMPL_PROPERTY_RO(cv::Mat, Labels, _labels)
00110 };
00111 
00112 
00113 void LBPH::load(const FileStorage& fs) {
00114     fs["radius"] >> _radius;
00115     fs["neighbors"] >> _neighbors;
00116     fs["grid_x"] >> _grid_x;
00117     fs["grid_y"] >> _grid_y;
00118     //read matrices
00119     readFileNodeList(fs["histograms"], _histograms);
00120     fs["labels"] >> _labels;
00121     const FileNode& fn = fs["labelsInfo"];
00122     if (fn.type() == FileNode::SEQ)
00123     {
00124         _labelsInfo.clear();
00125         for (FileNodeIterator it = fn.begin(); it != fn.end();)
00126         {
00127             LabelInfo item;
00128             it >> item;
00129             _labelsInfo.insert(std::make_pair(item.label, item.value));
00130         }
00131     }
00132 }
00133 
00134 // See FaceRecognizer::save.
00135 void LBPH::save(FileStorage& fs) const {
00136     
00137     /*the debug
00138     fs << "radius" << _radius;
00139     fs << "neighbors" << _neighbors;
00140     fs << "grid_x" << _grid_x;
00141     fs << "grid_y" << _grid_y;
00142     // write matrices
00143     writeFileNodeList(fs, "histograms", _histograms);
00144     fs << "labels" << _labels;
00145     fs << "labelsInfo" << "[";
00146     for (std::map<int, String>::const_iterator it = _labelsInfo.begin(); it != _labelsInfo.end(); it++)
00147         fs << LabelInfo(it->first, it->second);
00148     fs << "]";*/
00149 }
00150 
00151 void LBPH::train(InputArrayOfArrays _in_src, InputArray _in_labels) {
00152     this->train(_in_src, _in_labels, false);
00153 }
00154 
00155 void LBPH::update(InputArrayOfArrays _in_src, InputArray _in_labels) {
00156     // got no data, just return
00157     if(_in_src.total() == 0)
00158         return;
00159 
00160     this->train(_in_src, _in_labels, true);
00161 }
00162 
00163 
00164 //------------------------------------------------------------------------------
00165 // LBPH
00166 //------------------------------------------------------------------------------
00167 
00168 template <typename _Tp> static
00169 void olbp_(InputArray _src, OutputArray _dst) {
00170     // get matrices
00171     Mat src = _src.getMat();
00172     // allocate memory for result
00173     _dst.create(src.rows-2, src.cols-2, CV_8UC1);
00174     Mat dst = _dst.getMat();
00175     // zero the result matrix
00176     dst.setTo(0);
00177     // calculate patterns
00178     for(int i=1;i<src.rows-1;i++) {
00179         for(int j=1;j<src.cols-1;j++) {
00180             _Tp center = src.at<_Tp>(i,j);
00181             unsigned char code = 0;
00182             code |= (src.at<_Tp>(i-1,j-1) >= center) << 7;
00183             code |= (src.at<_Tp>(i-1,j) >= center) << 6;
00184             code |= (src.at<_Tp>(i-1,j+1) >= center) << 5;
00185             code |= (src.at<_Tp>(i,j+1) >= center) << 4;
00186             code |= (src.at<_Tp>(i+1,j+1) >= center) << 3;
00187             code |= (src.at<_Tp>(i+1,j) >= center) << 2;
00188             code |= (src.at<_Tp>(i+1,j-1) >= center) << 1;
00189             code |= (src.at<_Tp>(i,j-1) >= center) << 0;
00190             dst.at<unsigned char>(i-1,j-1) = code;
00191         }
00192     }
00193 }
00194 
00195 //------------------------------------------------------------------------------
00196 // cv::elbp
00197 //------------------------------------------------------------------------------
00198 template <typename _Tp> static
00199 inline void elbp_(InputArray _src, OutputArray _dst, int radius, int neighbors) {
00200     //get matrices
00201     Mat src = _src.getMat();
00202     // allocate memory for result
00203     _dst.create(src.rows-2*radius, src.cols-2*radius, CV_32SC1);
00204     Mat dst = _dst.getMat();
00205     // zero
00206     dst.setTo(0);
00207     for(int n=0; n<neighbors; n++) {
00208         // sample points
00209         float x = static_cast<float>(radius * cos(2.0*CV_PI*n/static_cast<float>(neighbors)));
00210         float y = static_cast<float>(-radius * sin(2.0*CV_PI*n/static_cast<float>(neighbors)));
00211         // relative indices
00212         int fx = static_cast<int>(floor(x));
00213         int fy = static_cast<int>(floor(y));
00214         int cx = static_cast<int>(ceil(x));
00215         int cy = static_cast<int>(ceil(y));
00216         // fractional part
00217         float ty = y - fy;
00218         float tx = x - fx;
00219         // set interpolation weights
00220         float w1 = (1 - tx) * (1 - ty);
00221         float w2 =      tx  * (1 - ty);
00222         float w3 = (1 - tx) *      ty;
00223         float w4 =      tx  *      ty;
00224         // iterate through your data
00225         for(int i=radius; i < src.rows-radius;i++) {
00226             for(int j=radius;j < src.cols-radius;j++) {
00227                 // calculate interpolated value
00228                 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));
00229                 // floating point precision, so check some machine-dependent epsilon
00230                 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;
00231             }
00232         }
00233     }
00234 }
00235 
00236 static void elbp(InputArray src, OutputArray dst, int radius, int neighbors)
00237 {
00238     int type = src.type();
00239     switch (type) {
00240     case CV_8SC1:   elbp_<char>(src,dst, radius, neighbors); break;
00241     case CV_8UC1:   elbp_<unsigned char>(src, dst, radius, neighbors); break;
00242     case CV_16SC1:  elbp_<short>(src,dst, radius, neighbors); break;
00243     case CV_16UC1:  elbp_<unsigned short>(src,dst, radius, neighbors); break;
00244     case CV_32SC1:  elbp_<int>(src,dst, radius, neighbors); break;
00245     case CV_32FC1:  elbp_<float>(src,dst, radius, neighbors); break;
00246     case CV_64FC1:  elbp_<double>(src,dst, radius, neighbors); break;
00247     default:
00248         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);
00249         CV_Error(Error::StsNotImplemented, error_msg);
00250         break;
00251     }
00252 }
00253 
00254 static Mat
00255 histc_(const Mat& src, int minVal=0, int maxVal=255, bool normed=false)
00256 {
00257     Mat result;
00258     // Establish the number of bins.
00259     int histSize = maxVal-minVal+1;
00260     // Set the ranges.
00261     float range[] = { static_cast<float>(minVal), static_cast<float>(maxVal+1) };
00262     const float* histRange = { range };
00263     // calc histogram
00264     calcHist(&src, 1, 0, Mat(), result, 1, &histSize, &histRange, true, false);
00265     // normalize
00266     if(normed) {
00267         result /= (int)src.total();
00268     }
00269     return result.reshape(1,1);
00270 }
00271 
00272 static Mat histc(InputArray _src, int minVal, int maxVal, bool normed)
00273 {
00274     Mat src = _src.getMat();
00275     switch (src.type()) {
00276         case CV_8SC1:
00277             return histc_(Mat_<float>(src), minVal, maxVal, normed);
00278             break;
00279         case CV_8UC1:
00280             return histc_(src, minVal, maxVal, normed);
00281             break;
00282         case CV_16SC1:
00283             return histc_(Mat_<float>(src), minVal, maxVal, normed);
00284             break;
00285         case CV_16UC1:
00286             return histc_(src, minVal, maxVal, normed);
00287             break;
00288         case CV_32SC1:
00289             return histc_(Mat_<float>(src), minVal, maxVal, normed);
00290             break;
00291         case CV_32FC1:
00292             return histc_(src, minVal, maxVal, normed);
00293             break;
00294         default:
00295             CV_Error(Error::StsUnmatchedFormats, "This type is not implemented yet."); break;
00296     }
00297     return Mat();
00298 }
00299 
00300 
00301 static Mat spatial_histogram(InputArray _src, int numPatterns,
00302                              int grid_x, int grid_y, bool /*normed*/)
00303 {
00304     Mat src = _src.getMat();
00305     // calculate LBP patch size
00306     int width = src.cols/grid_x;
00307     int height = src.rows/grid_y;
00308     // allocate memory for the spatial histogram
00309     Mat result = Mat::zeros(grid_x * grid_y, numPatterns, CV_32FC1);
00310     // return matrix with zeros if no data was given
00311     if(src.empty())
00312         return result.reshape(1,1);
00313     // initial result_row
00314     int resultRowIdx = 0;
00315     // iterate through grid
00316     for(int i = 0; i < grid_y; i++) {
00317         for(int j = 0; j < grid_x; j++) {
00318             Mat src_cell = Mat(src, Range(i*height,(i+1)*height), Range(j*width,(j+1)*width));
00319             Mat cell_hist = histc(src_cell, 0, (numPatterns-1), true);
00320             // copy to the result matrix
00321             Mat result_row = result.row(resultRowIdx);
00322             cell_hist.reshape(1,1).convertTo(result_row, CV_32FC1);
00323             // increase row count in result matrix
00324             resultRowIdx++;
00325         }
00326     }
00327     // return result as reshaped feature vector
00328     return result.reshape(1,1);
00329 }
00330 
00331 //------------------------------------------------------------------------------
00332 // wrapper to cv::elbp (extended local binary patterns)
00333 //------------------------------------------------------------------------------
00334 
00335 static Mat elbp(InputArray src, int radius, int neighbors) {
00336     Mat dst;
00337     elbp(src, dst, radius, neighbors);
00338     return dst;
00339 }
00340 
00341 void LBPH::train(InputArrayOfArrays _in_src, InputArray _in_labels, bool preserveData) {
00342     if(_in_src.kind() != _InputArray::STD_VECTOR_MAT && _in_src.kind() != _InputArray::STD_VECTOR_VECTOR) {
00343         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<...> >).";
00344         CV_Error(Error::StsBadArg, error_message);
00345     }
00346     if(_in_src.total() == 0) {
00347         String error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
00348         CV_Error(Error::StsUnsupportedFormat, error_message);
00349     } else if(_in_labels.getMat().type() != CV_32SC1) {
00350         String error_message = format("Labels must be given as integer (CV_32SC1). Expected %d, but was %d.", CV_32SC1, _in_labels.type());
00351         CV_Error(Error::StsUnsupportedFormat, error_message);
00352     }
00353     // get the vector of matrices
00354     std::vector<Mat> src;
00355     _in_src.getMatVector(src);
00356     // get the label matrix
00357     Mat labels = _in_labels.getMat();
00358     // check if data is well- aligned
00359     if(labels.total() != src.size()) {
00360         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());
00361         CV_Error(Error::StsBadArg, error_message);
00362     }
00363     // if this model should be trained without preserving old data, delete old model data
00364     if(!preserveData) {
00365         _labels.release();
00366         _histograms.clear();
00367     }
00368     // append labels to _labels matrix
00369     for(size_t labelIdx = 0; labelIdx < labels.total(); labelIdx++) {
00370         _labels.push_back(labels.at<int>((int)labelIdx));
00371     }
00372     // store the spatial histograms of the original data
00373     for(size_t sampleIdx = 0; sampleIdx < src.size(); sampleIdx++) {
00374         // calculate lbp image
00375         Mat lbp_image = elbp(src[sampleIdx], _radius, _neighbors);
00376         // get spatial histogram from this lbp image
00377         Mat p = spatial_histogram(
00378                 lbp_image, /* lbp_image */
00379                 static_cast<int>(std::pow(2.0, static_cast<double>(_neighbors))), /* number of possible patterns */
00380                 _grid_x, /* grid size x */
00381                 _grid_y, /* grid size y */
00382                 true);
00383         // add to templates
00384         _histograms.push_back(p);
00385     }
00386 }
00387 
00388 void LBPH::predict(InputArray _src, Ptr<PredictCollector> collector) const {
00389     if(_histograms.empty()) {
00390         // throw error if no data (or simply return -1?)
00391         String error_message = "This LBPH model is not computed yet. Did you call the train method?";
00392         CV_Error(Error::StsBadArg, error_message);
00393     }
00394     Mat src = _src.getMat();
00395     // get the spatial histogram from input image
00396     Mat lbp_image = elbp(src, _radius, _neighbors);
00397     Mat query = spatial_histogram(
00398             lbp_image, /* lbp_image */
00399             static_cast<int>(std::pow(2.0, static_cast<double>(_neighbors))), /* number of possible patterns */
00400             _grid_x, /* grid size x */
00401             _grid_y, /* grid size y */
00402             true /* normed histograms */);
00403     // find 1-nearest neighbor
00404     collector->init((int)_histograms.size());
00405     for (size_t sampleIdx = 0; sampleIdx < _histograms.size(); sampleIdx++) {
00406         double dist = compareHist(_histograms[sampleIdx], query, HISTCMP_CHISQR_ALT);
00407         int label = _labels.at<int>((int)sampleIdx);
00408         if (!collector->collect(label, dist))return;
00409     }
00410 }
00411 
00412 Ptr<LBPHFaceRecognizer> createLBPHFaceRecognizer (int radius, int neighbors,
00413                                              int grid_x, int grid_y, double threshold)
00414 {
00415     return makePtr<LBPH>(radius, neighbors, grid_x, grid_y, threshold);
00416 }
00417 
00418 
00419 }}
00420