Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of gr-peach-opencv-project-sd-card by
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
Generated on Tue Jul 12 2022 14:47:12 by
