00001 /*!@file Features/LocalBinaryPatterns.C Multiclass LocalBinaryPatterns Classifier module */ 00002 // //////////////////////////////////////////////////////////////////// // 00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00004 // University of Southern California (USC) and the iLab at USC. // 00005 // See http://iLab.usc.edu for information about this project. // 00006 // //////////////////////////////////////////////////////////////////// // 00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00009 // in Visual Environments, and Applications'' by Christof Koch and // 00010 // Laurent Itti, California Institute of Technology, 2001 (patent // 00011 // pending; application number 09/912,225 filed July 23, 2001; see // 00012 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00013 // //////////////////////////////////////////////////////////////////// // 00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00015 // // 00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00017 // redistribute it and/or modify it under the terms of the GNU General // 00018 // Public License as published by the Free Software Foundation; either // 00019 // version 2 of the License, or (at your option) any later version. // 00020 // // 00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00024 // PURPOSE. See the GNU General Public License for more details. // 00025 // // 00026 // You should have received a copy of the GNU General Public License // 00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00029 // Boston, MA 02111-1307 USA. // 00030 // //////////////////////////////////////////////////////////////////// // 00031 // 00032 // Primary maintainer for this file: Dan Parks <danielfp@usc.edu> 00033 // $HeadURL$ 00034 // $Id$ 00035 // 00036 00037 #include <fstream> 00038 #include <iostream> 00039 #include <iomanip> 00040 #include <string> 00041 #include <cstdlib> 00042 #include <map> 00043 00044 #include "LocalBinaryPatterns.H" 00045 #include "Component/ModelComponent.H" 00046 #include "Component/ModelParam.H" 00047 #include "Component/OptionManager.H" 00048 #include "Image/ColorOps.H" 00049 #include "Image/CutPaste.H" 00050 00051 #define LOG_OFFSET 1.0F 00052 00053 namespace 00054 { 00055 inline int lbpsign(int x) 00056 { 00057 return (((x)<0) ? 0 : 1); 00058 } 00059 00060 }; 00061 00062 00063 //! Constructor 00064 LocalBinaryPatterns::LocalBinaryPatterns(int LBPRadius, int LBPPixels, int varBins, bool useColor, bool normalize) : 00065 itsLBPRadius(LBPRadius), 00066 itsLBPPixels(LBPPixels), 00067 itsVarBins(varBins), 00068 itsUseColor(useColor), 00069 itsNormalize(normalize) 00070 { 00071 if(itsUseColor) 00072 itsColorBins = 2; 00073 else 00074 itsColorBins = 0; 00075 } 00076 00077 //! Destructor 00078 LocalBinaryPatterns::~LocalBinaryPatterns() 00079 { 00080 } 00081 00082 00083 00084 //! Add model to 00085 00086 void LocalBinaryPatterns::addModel(const Image<PixRGB<byte> >& texture, const int id) 00087 { 00088 std::vector<float> lbp,col,var; 00089 createRawHistogram(texture,lbp,col,var); 00090 // Bin LBP in a temp list until we are ready to build the complete histogram 00091 addModel(lbp,col,var,id); 00092 } 00093 00094 void LocalBinaryPatterns::addModel(const std::vector<float>& lbp, const std::vector<float> &col, const std::vector<float>& var, const int id) 00095 { 00096 // Store LBP and color bins 00097 itsTempLBP[id].push_back(lbp); 00098 itsTempColor[id].push_back(col); 00099 // Sort variance, to speed up build process 00100 std::vector<float> svar = var; 00101 std::sort(svar.begin(),svar.end()); 00102 // Store Variance values temporarily until we can get the total distribution of variances 00103 itsTempVariance[id].push_back(svar); 00104 //LINFO("Number of models is now %Zu, for id %d, num exemplars %Zu ",itsTempLBP.size(),id,itsTempLBP[id].size()); 00105 } 00106 00107 std::vector<float> LocalBinaryPatterns::createHistogram(const Image<PixRGB<byte> >& texture) 00108 { 00109 std::vector<float> lbp,col,var; 00110 createRawHistogram(texture,lbp,col,var); 00111 std::vector<float> varHist = convertVariance(var); 00112 std::vector<float> hist = lbp; 00113 hist.insert(hist.end(),col.begin(),col.end()); 00114 hist.insert(hist.end(),varHist.begin(),varHist.end()); 00115 return hist; 00116 } 00117 00118 00119 void LocalBinaryPatterns::createRawHistogram(const Image<PixRGB<byte> >& colTexture, std::vector<float>& lbps, std::vector<float>& col, std::vector<float>& vars) 00120 { 00121 Image<float> texture = luminance(colTexture); 00122 int w = texture.getWidth(), h = texture.getHeight(); 00123 lbps.resize(itsLBPPixels+2); 00124 float step = 1;//itsLBPPixels*2+1; 00125 ASSERT(2*itsLBPRadius < std::min(w,h)); 00126 float *g = new float[itsLBPPixels]; 00127 int *sg = new int[itsLBPPixels]; 00128 int sumEntries=0; 00129 for (float y = itsLBPRadius; y < h-itsLBPRadius; y+=step) 00130 { 00131 for (float x = itsLBPRadius; x < w-itsLBPRadius; x+=step) 00132 { 00133 float var=0; 00134 //float lbp=0; 00135 uint diff=0; 00136 float uniform=0; 00137 float mu=0; 00138 float gc = texture.getValInterp(x,y); 00139 for(int p=0;p<itsLBPPixels;p++) 00140 { 00141 g[p] = texture.getValInterp(x-itsLBPRadius*sin(2.0*M_PI*p/itsLBPPixels),y+itsLBPRadius*cos(2.0*M_PI*p/itsLBPPixels)); 00142 mu += g[p]; 00143 sg[p] = lbpsign(g[p]-gc); 00144 // If want to calculate full LBP, need to do 00145 //lbp += lbpsign(g[p])*pow(2,p); 00146 // then circular bit shift until minimum value is found 00147 // Here we will just bin the "uniform" patterns in addition to the variance 00148 diff += sg[p]; 00149 } 00150 for(int p=0;p<itsLBPPixels;p++) 00151 { 00152 if(p==0) uniform += abs(sg[itsLBPPixels-1]-sg[0]); 00153 else uniform += abs(sg[p]-sg[p-1]); 00154 var += (sg[p]-mu)*(sg[p]-mu); 00155 } 00156 // Bin into one of the uniform bins or into the "other" bin 00157 sumEntries++; 00158 if(uniform<=2) lbps[diff]++; 00159 else lbps[itsLBPPixels+1]++; 00160 // Normalize and add to list of variances (have to wait to bin variances until a total distribution is determined) 00161 if(itsVarBins > 0) 00162 vars.push_back(var / itsLBPPixels); 00163 } 00164 } 00165 // Normalize values in lbps histogram 00166 if(itsNormalize) 00167 { 00168 for(uint b=0;b<lbps.size();b++) 00169 { 00170 lbps[b]=lbps[b]/float(sumEntries)+LOG_OFFSET; 00171 } 00172 } 00173 delete[] g; 00174 delete[] sg; 00175 00176 // If using color, add color 00177 00178 if(itsUseColor) 00179 { 00180 float rg,by,rgbysum; 00181 colorSum(colTexture,colTexture.getBounds(),rg,by); 00182 if(itsNormalize) 00183 { 00184 rgbysum = rg+by; 00185 col.push_back(rg/rgbysum+LOG_OFFSET); 00186 col.push_back(by/rgbysum+LOG_OFFSET); 00187 } 00188 else 00189 { 00190 col.push_back(rg); 00191 col.push_back(by); 00192 } 00193 } 00194 } 00195 00196 00197 void LocalBinaryPatterns::getLabeledData(const MapModelVector& models, std::vector<std::vector<float> >& data, std::vector<float>& labels) 00198 { 00199 data.clear(); 00200 labels.clear(); 00201 MapModelVector::const_iterator mitr=models.begin(), mstop = models.end(); 00202 int idx=0; 00203 for(;mitr!=mstop;mitr++) 00204 { 00205 // Iterate through model exemplars 00206 for(uint i=0;i<mitr->second.size();i++) 00207 { 00208 labels.push_back(mitr->first); 00209 data.push_back(std::vector<float>()); 00210 data[idx] = mitr->second[i]; 00211 idx++; 00212 } 00213 } 00214 00215 } 00216 00217 void LocalBinaryPatterns::colorSum(Image<PixRGB<byte> >img, Rectangle rec, float& rg, float& by) 00218 { 00219 ASSERT(img.rectangleOk(rec)); 00220 const Image<PixRGB<byte> > subImg = crop(img,rec); 00221 Image<float> l,a,b; 00222 getLAB(subImg,l,a,b); 00223 rg=0; by=0; 00224 Dims dims = subImg.getDims(); 00225 00226 for (int y = 0; y < dims.h(); ++y) 00227 { 00228 for (int x = 0; x < dims.w(); ++x) 00229 { 00230 rg+=(a.getVal(x,y)+128); 00231 by+=(b.getVal(x,y)+128); 00232 } 00233 } 00234 00235 } 00236 00237 00238 00239 std::vector<float> LocalBinaryPatterns::convertVariance(const std::vector<float>& vars) 00240 { 00241 std::vector<float> varHist = std::vector<float>(vars.size()); 00242 // Convert the variance values into a histogram based on the bin break points 00243 varHist.resize(itsVarBins); 00244 if(itsVarBins == 0) 00245 return varHist; 00246 int sumEntries=vars.size(); 00247 int binHint = -1; 00248 for(uint i=0;i<vars.size();i++) 00249 { 00250 // Optimized version 00251 int bin = getVarIndex<std::vector<float> >(itsVarBinThresholds,vars[i],binHint); 00252 varHist[bin]++; 00253 binHint=bin; 00254 } 00255 // Normalize values in var histogram 00256 for(uint b=0;b<varHist.size();b++) 00257 { 00258 varHist[b]=varHist[b]/float(sumEntries)+LOG_OFFSET; 00259 } 00260 return varHist; 00261 } 00262 00263 LocalBinaryPatterns::MapModelVector LocalBinaryPatterns::getModels() 00264 { 00265 return itsModels; 00266 } 00267 00268 void LocalBinaryPatterns::setModels(const MapModelVector& models) 00269 { 00270 itsModels=models; 00271 } 00272 00273 std::vector<float> LocalBinaryPatterns::getVarThresholds() 00274 { 00275 return itsVarBinThresholds; 00276 } 00277 00278 void LocalBinaryPatterns::setVarThresholds(std::vector<float> thresholds) 00279 { 00280 ASSERT(thresholds.size()==(uint)std::max(0,itsVarBins-1)); 00281 itsVarBinThresholds = thresholds; 00282 } 00283 00284 00285 void LocalBinaryPatterns::setIncompleteModels(const MapModelVector& incompleteModels) 00286 { 00287 // Need to split the vectors into an LBP operator and a variance list based on the size of the LBP operator histogram 00288 MapModelVector::const_iterator imitr=incompleteModels.begin(), imstop = incompleteModels.end(); 00289 // Iterate through model ids 00290 for(;imitr!=imstop;imitr++) 00291 { 00292 // Iterate through model exemplars 00293 for(uint i=0;i<imitr->second.size();i++) 00294 { 00295 std::vector<float> combinedHist = imitr->second[i]; 00296 // Split according to length of LBP histogram 00297 std::vector<float> lbp,col,tmpVar; 00298 lbp.insert(lbp.begin(),combinedHist.begin(),combinedHist.begin()+itsLBPPixels+2); 00299 col.insert(col.begin(),combinedHist.begin()+itsLBPPixels+2,combinedHist.begin()+itsLBPPixels+2+itsColorBins); 00300 tmpVar.insert(tmpVar.begin(),combinedHist.begin()+itsLBPPixels+2+itsColorBins,combinedHist.end()); 00301 addModel(lbp,col,tmpVar,imitr->first); 00302 } 00303 } 00304 } 00305 00306 LocalBinaryPatterns::MapModelVector LocalBinaryPatterns::getIncompleteModels() 00307 { 00308 MapModelVector models; 00309 MapModelVector::const_iterator imitr=itsTempLBP.begin(), imstop = itsTempLBP.end(); 00310 // Iterate through model ids 00311 for(;imitr!=imstop;imitr++) 00312 { 00313 // Iterate through model exemplars 00314 for(uint i=0;i<imitr->second.size();i++) 00315 { 00316 std::vector<float> lbp = imitr->second[i]; 00317 std::vector<float> col = itsTempColor[imitr->first][i]; 00318 std::vector<float> tmpVar = itsTempVariance[imitr->first][i]; 00319 // Combined lbp, col, and var 00320 std::vector<float> combinedHist = lbp; 00321 combinedHist.insert(combinedHist.end(),col.begin(),col.end()); 00322 combinedHist.insert(combinedHist.end(),tmpVar.begin(),tmpVar.end()); 00323 models[imitr->first].push_back(combinedHist); 00324 } 00325 } 00326 return models; 00327 } 00328 00329 void LocalBinaryPatterns::combineModels(const std::vector< MapModelVector >& allModels, MapModelVector& combined) 00330 { 00331 for(uint i=0;i<allModels.size();i++) 00332 { 00333 appendMap(combined,allModels[i]); 00334 } 00335 } 00336 00337 void LocalBinaryPatterns::appendMap(MapModelVector& dst, const MapModelVector& src) 00338 { 00339 MapModelVector::const_iterator sitr=src.begin(), sstop = src.end(); 00340 // Iterate through model ids 00341 for(;sitr!=sstop;sitr++) 00342 { 00343 // Iterate through model exemplars 00344 for(uint i=0;i<sitr->second.size();i++) 00345 { 00346 ASSERT(dst[sitr->first].size() <= sitr->second.size()); 00347 // Allocate space if needed 00348 if(dst[sitr->first].size() < sitr->second.size()) 00349 dst[sitr->first].resize(sitr->second.size()); 00350 // Append map exemplar to end of original exemplar 00351 dst[sitr->first][i].insert(dst[sitr->first][i].end(),sitr->second[i].begin(),sitr->second[i].end()); 00352 } 00353 } 00354 } 00355 00356 std::vector<float> LocalBinaryPatterns::merge(const std::vector<float>& left, const std::vector<float>& right) 00357 { 00358 // Fill the resultant vector with sorted results from both vectors 00359 std::vector<float> result; 00360 unsigned left_it = 0, right_it = 0; 00361 00362 while(left_it < left.size() && right_it < right.size()) 00363 { 00364 // If the left value is smaller than the right it goes next 00365 // into the resultant vector 00366 if(left[left_it] < right[right_it]) 00367 { 00368 result.push_back(left[left_it]); 00369 left_it++; 00370 } 00371 else 00372 { 00373 result.push_back(right[right_it]); 00374 right_it++; 00375 } 00376 } 00377 00378 // Push the remaining data from both vectors onto the resultant 00379 while(left_it < left.size()) 00380 { 00381 result.push_back(left[left_it]); 00382 left_it++; 00383 } 00384 00385 while(right_it < right.size()) 00386 { 00387 result.push_back(right[right_it]); 00388 right_it++; 00389 } 00390 00391 return result; 00392 } 00393 00394 void LocalBinaryPatterns::buildModels() 00395 { 00396 itsVarBinThresholds.clear(); 00397 if(itsVarBins > 0) 00398 { 00399 // Build total variance distribution, note this assumes that individual variance vectors were presorted 00400 std::vector<float> totalDist; 00401 MapModelVector::const_iterator tvitr=itsTempVariance.begin(), tvstop = itsTempVariance.end(); 00402 for(;tvitr!=tvstop;tvitr++) 00403 { 00404 for(uint i=0;i<tvitr->second.size();i++) 00405 { 00406 totalDist = merge(totalDist,tvitr->second[i]); 00407 } 00408 } 00409 // Determine number of elements to skip to collect thresholds 00410 float step = totalDist.size()/float(itsVarBins); 00411 // Grab thresholds (might want to interpolate them) 00412 for(int s=1;s<itsVarBins;s++) 00413 { 00414 float thresh=totalDist[floor(step*s)]; 00415 itsVarBinThresholds.push_back(thresh); 00416 } 00417 } 00418 itsModels.clear(); 00419 convertIncompleteModels(); 00420 } 00421 00422 void LocalBinaryPatterns::convertIncompleteModels() 00423 { 00424 MapModelVector varHist; 00425 MapModelVector::const_iterator tvitr=itsTempVariance.begin(), tvstop = itsTempVariance.end(); 00426 if(itsVarBins > 0) 00427 { 00428 // Convert model variance data to histograms 00429 for(tvitr=itsTempVariance.begin();tvitr!=tvstop;tvitr++) 00430 { 00431 std::vector<std::vector<float> > bins = std::vector<std::vector<float> > ((*tvitr).second.size()); 00432 for(uint i=0;i<tvitr->second.size();i++) 00433 { 00434 bins[i] = convertVariance(tvitr->second[i]); 00435 } 00436 varHist[tvitr->first] = bins; 00437 } 00438 } 00439 00440 MapModelVector::const_iterator tmitr=itsTempLBP.begin(), tmstop = itsTempLBP.end(); 00441 for(;tmitr!=tmstop;tmitr++) 00442 { 00443 for(uint i=0;i<tmitr->second.size();i++) 00444 { 00445 std::vector<float> hist = tmitr->second[i]; 00446 std::vector<float> col = itsTempColor[tmitr->first][i]; 00447 // Add color 00448 hist.insert(hist.end(),col.begin(),col.end()); 00449 // Add variance if valid 00450 if(itsVarBins > 0) 00451 hist.insert(hist.end(),varHist[tmitr->first][i].begin(),varHist[tmitr->first][i].end()); 00452 // Initialize if no key is present 00453 if(itsModels.find(tmitr->first) == itsModels.end()) 00454 itsModels[tmitr->first] = std::vector<std::vector<float> >(); 00455 itsModels[tmitr->first].push_back(hist); 00456 } 00457 } 00458 00459 } 00460 00461 00462 template<class T> int LocalBinaryPatterns::getVarIndex(const T& thresh, float var, int binHint) 00463 { 00464 // Optimization, if variance is pre-sorted 00465 if(binHint >=0) 00466 { 00467 if(binHint < int(thresh.size()) ) 00468 { 00469 if(var < thresh[binHint]) 00470 { 00471 if(binHint == 0) 00472 return binHint; 00473 else if(var > thresh[binHint-1]) 00474 return binHint; 00475 } 00476 } 00477 else if(var > thresh[binHint-1]) 00478 { 00479 return binHint; 00480 } 00481 } 00482 // Binary search to find correct bin 00483 bool binFound=false; 00484 const int endBin = thresh.size(); 00485 // Check if only one bin 00486 if(endBin==0) 00487 return 0; 00488 int lowBin = 0, highBin = endBin; 00489 int curBin = (lowBin+highBin)/2; 00490 while(!binFound) 00491 { 00492 00493 if(var > thresh[curBin]) 00494 lowBin = curBin+1; 00495 else 00496 highBin = curBin; 00497 curBin = (lowBin+highBin)/2; 00498 if( highBin - lowBin <= 1) 00499 { 00500 binFound=true; 00501 } 00502 } 00503 if(var > thresh[lowBin]) 00504 curBin = highBin; 00505 else 00506 curBin = std::min(lowBin,endBin); 00507 return curBin; 00508 } 00509 00510 //! Get number of models 00511 uint LocalBinaryPatterns::getTotalModelExemplars(MapModelVector models) 00512 { 00513 MapModelVector::const_iterator mmitr = models.begin(), mmstop = models.end(); 00514 uint cnt=0; 00515 for(;mmitr!=mmstop;mmitr++) 00516 { 00517 cnt += mmitr->second.size(); 00518 } 00519 return cnt; 00520 } 00521 00522 00523 LocalBinaryPatterns::MapModelVector LocalBinaryPatterns::readModelsFile(std::string modelsFile) 00524 { 00525 FILE *fmodel; 00526 int numEntries, numColumns; 00527 MapModelVector models; 00528 00529 fmodel = fopen(modelsFile.c_str(),"r"); 00530 if(fmodel == NULL) 00531 { 00532 LFATAL("Unable to open LBP models file"); 00533 } 00534 numEntries=0; 00535 while(1) 00536 { 00537 int id; 00538 if(fscanf(fmodel, "%d %d ", &id,&numColumns)!= 2) 00539 { 00540 LINFO("Read %d samples in model file %s",numEntries,modelsFile.c_str()); 00541 break; 00542 } 00543 std::vector<float> hist; 00544 for(int c=0;c<numColumns;c++) 00545 { 00546 float tmp; 00547 if(fscanf(fmodel, "%f ", &tmp)!= 1) LFATAL("Failed to load column %d in row %d of file: %s",c,numEntries,modelsFile.c_str()); 00548 hist.push_back(tmp); 00549 } 00550 if(fscanf(fmodel,"\n")!= 0) LFATAL("Failed to parse newline at end of row %d of file: %s",numEntries,modelsFile.c_str()); 00551 // Initialize if no key is present 00552 if(models.find(id) == models.end()) 00553 models[id] = std::vector<std::vector<float> >(); 00554 models[id].push_back(hist); 00555 numEntries++; 00556 } 00557 fclose(fmodel); 00558 return models; 00559 } 00560 00561 void LocalBinaryPatterns::writeModelsFile(std::string modelsFile, MapModelVector models) 00562 { 00563 std::ofstream outfile; 00564 outfile.open(modelsFile.c_str(),std::ios::out); 00565 if (outfile.is_open()) 00566 { 00567 MapModelVector::const_iterator mmitr = models.begin(), mmstop = models.end(); 00568 for(;mmitr!=mmstop;mmitr++) 00569 { 00570 for(uint i=0;i<mmitr->second.size();i++) 00571 { 00572 outfile << mmitr->first << " " << mmitr->second[i].size() << " "; 00573 for(uint j=0;j<mmitr->second[i].size();j++) 00574 { 00575 outfile << std::setiosflags(std::ios::fixed) << std::setprecision(4) << mmitr->second[i][j] << " "; 00576 } 00577 outfile << std::endl; 00578 } 00579 } 00580 outfile.close(); 00581 } 00582 else 00583 { 00584 LFATAL("Could not open LBP Models output file"); 00585 } 00586 } 00587 00588 00589 std::vector<float> LocalBinaryPatterns::readThresholdsFile(std::string thresholdsFile) 00590 { 00591 FILE *fthresh; 00592 int numEntries; 00593 std::vector<float> thresholds; 00594 00595 fthresh = fopen(thresholdsFile.c_str(),"r"); 00596 if(fthresh == NULL) 00597 { 00598 LFATAL("Unable to open Bin Thresholds input file"); 00599 } 00600 00601 if(fscanf(fthresh, "%d\n", &numEntries) != 1) LFATAL("Failed to load number of entries from: %s", thresholdsFile.c_str()); 00602 for(int i=0;i<numEntries;i++) 00603 { 00604 float tmp; 00605 if(fscanf(fthresh, "%f ", &tmp)!= 1) LFATAL("Failed to load threshold for bin %d in file: %s",i,thresholdsFile.c_str()); 00606 thresholds.push_back(tmp); 00607 } 00608 if(fscanf(fthresh,"\n")!= 0) LFATAL("Failed to parse newline at end of bin thresholds in file: %s",thresholdsFile.c_str()); 00609 fclose(fthresh); 00610 return thresholds; 00611 } 00612 00613 void LocalBinaryPatterns::writeThresholdsFile(std::string thresholdsFile, std::vector<float> thresholds) 00614 { 00615 std::ofstream outfile; 00616 outfile.open(thresholdsFile.c_str(),std::ios::out); 00617 if (outfile.is_open()) 00618 { 00619 outfile << thresholds.size() << " "; 00620 outfile << std::endl; 00621 for(uint i=0;i<thresholds.size();i++) 00622 { 00623 outfile << std::setiosflags(std::ios::fixed) << std::setprecision(4) << thresholds[i] << " "; 00624 } 00625 outfile << std::endl; 00626 outfile.close(); 00627 } 00628 else 00629 { 00630 LFATAL("Could not open Bin Thresholds output file"); 00631 } 00632 } 00633 00634 00635 // Template instantiations for getVarIndex 00636 template int LocalBinaryPatterns::getVarIndex(const std::vector<float> &thresh, float var, int binHint); 00637 00638 template int LocalBinaryPatterns::getVarIndex(const std::deque<float> &thresh, float var, int binHint);