Viola-Jones人脸检测--AdaptBoost特征选择

矫情吗;* 2022-01-09 15:34 357阅读 0赞

Viola-Jones人脸检测算法的伟大之处不不仅仅在于其算法的实时效果,更重要的是其提出了解决目标检测这一类问题的一种通用思路。该算法有两个亮点,一个是积分图技术,一个是Cascade训练模型,一经提出便引起了极大关注,在很多优秀的论文中都能看到他们的身影。如TLD算法中Detector部分,以及BING objectness训练时的两层SVM模型等,很难说这没有受到Viola-Jones算法的影响。下面就来介绍构成Cascade模型的其中的一个基本元素AdaptBoost吧。

AdaptBoost并不是Viola-Jones的原创算法,它是机器学习领域的产物,属于Ensemble Learning中boosting的类别。Ensemble类的学习算法分为bagging和boosting两个类别,都是基于弱分类器构造强分类器的思想,其中bagging的代表算法是RandomForests,boosting的代表算法是AdaptBoost。这里推荐一篇论文,介绍AdaptBoost算法理论的,《A Brief Introduction to Boosting》。

本着分享交流的目的,下面的内容包括对AdaptBoost算法的理论介绍及给出用标准C++实现AdaptBoost的代码。对于不想依赖特定库的伙伴们来说,标准C++的这个版本是个不错的选择。如果有什么不正确的地方,请多多指教。

1.AdaptBoost原理

我们知道对于一个给定窗口大小的图像,其Harr特征的维度是很高的,如果用直接用对训练样本计算出的Harr特征来训练分类器这是不太可行的,我们需要对高维的Harr特征进行选择,选择部分来进行分类器的训练。而AdaptBoost恰好就符合这样的思想,其基本思想是由弱分类器构造强分类器,用弱分类器的联合分类结果作为强分类器的结果。AdaptBoost的弱分类器可以是一个stump,也就是树桩的意思,就是一个弱分类器是一个二分类树。在众多维的Harr特征中进行特征选择的方法是,要求选择一个特征,及选择一个该特征下用于二分类的阈值,如果在该特征和阈值下对训练样本的分类误差最小,就以该特征和其二分类阈值作为一个训练好的弱分类器,算法的具体实现可以参看实现部分的bestStump()接口。在每一次为弱分类器选择特征完成后,对于用于训练的样本的分布(也就是各样本的权重,初始值一般是相等的,都是1/N,N为样本个数)进行更新,每次的更新是由上一次的弱分类器的分类结果确定的,对于上一次弱分类器判断错误的样本,其权重会增大,判断正确的样本其权重会减小。AdaptBoost与RandomForest的一个区别是,在计算强分类器的结果时,AdaptBoost的弱分类器的权重是不一样的,而RandomForest的弱分类器的权重是相等的。

AdaptBoost算法的伪代码描述如下:

wKioL1U7gcji6QY0AAD7nu0zyTE143.jpg

2.标准C++实现

下面的这个接口部分,包含train的接口不包含test的部分,你可以在这个基础上增加test的接口部分。

  1. #ifndef _ADAPTBOOST_H_
  2. #define _ADAPTBOOST_H_
  3. #include <vector>
  4. #include <utility>
  5. #include <cmath>
  6. using namespace std;
  7. /**
  8. * @brief decision stump declaration
  9. *
  10. * @param featureIndex
  11. * @param weightedError achieved weighted error
  12. * @param threshold
  13. * @param margin achieved margin
  14. * @param toggle +1 or -1
  15. */
  16. struct StumpRule{
  17. int featureIndex;
  18. long double weightedError;
  19. double threshold;
  20. float margin;
  21. int toggle;
  22. };
  23. /**
  24.  * @brief what's inside AdaptBoost
  25.  *
  26.  * @param nPositives number of positive examples
  27.  * @param nNegatives number of negative examples
  28.  * @param initialPositiveWeight how much weight we give to positives at the outset
  29.  * @param ascendingFeatures for each feature, we have (float feature value, int exampleIndex)
  30.  *
  31.  * @param sampleCount nPositives + nNegatives
  32.  * @param inTrain is this a training set or a validation set
  33.  * @param exponentialRisk exponential risk for training set
  34.  * @param positiveTotalWeight total weight received by positive examples currently
  35.  * @param negativeTotalWeight total weight received by negative examples currently
  36.  * @param minWeight minimum weight among all weights currently
  37.  * @param maxWeight maximum weight among all weights currently
  38.  * @param weights weight vector for all examples involved
  39.  * @param labels are they positive or negative examples
  40.  * @param featureCount how many features are there
  41.  * @param committee what's the learned committee
  42.  */
  43. class AdaptBoost{
  44. private:
  45. int nPositives;
  46. int nNegatives;
  47. long double initialPositiveWeight;
  48. vector< vector<pair<float, int>> > ascendingFeatures;
  49. int sampleCount;
  50. int featureCount;
  51. long double positiveTotalWeight;
  52. long double negativeTotalWeight;
  53. long double minWeight;
  54. long double maxWeight;
  55. long double exponentialRisk;
  56. vector<double> weights;
  57. vector<int> labels;
  58. vector<StumpRule> committee;
  59. /**
  60. * @brief prevent copy and assignment
  61. */
  62. AdaptBoost(const AdaptBoost&);
  63. AdaptBoost operator=(const AdaptBoost&);
  64. protected:
  65. /**
  66. * @brief return for an element pointed by iterator and featureIndex its exampleIndex
  67. */
  68. int getTrainingExampleIndex(int featureIndex, int iterator);
  69. /**
  70. * @brief return for an element pointed by iterator and featureIndex its example value
  71. */
  72. float getTrainingExampleFeature(int featureIndex, int iterator);
  73. /**
  74. * @brief sort each featrue from different samples
  75. */
  76. void sortFeatures(
  77. vector< vector<pair<float, int>> >& features
  78. );
  79. /**
  80. * @brief best stump given a feature
  81. */
  82. void decisionStump(
  83. int featureIndex
  84. , StumpRule & best
  85. );
  86. /**
  87. * @brief best stump among all features
  88. */
  89. StumpRule bestStump();
  90. public:
  91. /**
  92. * @brief constructor
  93. * @param nPositives number of positives for training examples
  94. * @param nNegatives number of negatives for training examples
  95. * @param initialPositiveWeight initial weight of positives
  96. * @param data for training examples, positves front and negatives back
  97. */
  98. AdaptBoost(
  99. int nPositives
  100. , int nNegatives
  101. , long double initialPositiveWeight
  102. , const vector< vector<float> >& data
  103. );
  104. /**
  105. * @brief destructor
  106. */
  107. ~AdaptBoost();
  108. /**
  109. * @brief perform one round of adaboost
  110. */
  111. void oneRoundOfAdaboostTraining();
  112. /**
  113. * @brief get committee adaptboost trained
  114. */
  115. vector<StumpRule> getCommittee() {
  116. return committee;
  117. }
  118. /**
  119. * @brief get committee size
  120. */
  121. int getCommitteeSize() {
  122. return committee.size();
  123. }
  124. /**
  125. * @brief given the number of weak classifiers train for a committee
  126. * @param numOfWeakClassifier for number of weak classifiers of adapt boost
  127. */
  128. void adaptBoostTraining(int numOfWeakClassifier);
  129. /**
  130. * @brief evaluate how the committee fares on a training dataset
  131. *
  132. * @param tweak for predictLableOfTrainingExamples
  133. * @return falsePositive
  134. * @return detectionRate
  135. * @vector<int> return a blackList,if element of balckList is 0, then it means that
  136. * this sample could be used again otherwise it means not usable
  137. */
  138. vector<int> calcEmpiricalErrorInAdaBoostTraining(
  139. float tweak
  140. , float & falsePositive
  141. , float & detectionRate
  142. );
  143. /**
  144. * @brief given a tweak and a committe, what prediction do you make as to the training examples
  145. *
  146. * @param thresholdTweak tweak
  147. * @return prediction
  148. * @param onlyMostRecent use all the committee or its most recent member (a weak learner)
  149. */
  150. void predictLabelOfTrainingExamples(
  151. float tweakThreshold
  152. , vector<int> & prediction
  153. , bool onlyMostRecent=false
  154. );
  155. };
  156. #endif
  157. #include <cassert>
  158. #include <algorithm>
  159. #include <iostream>
  160. #include <iomanip>
  161. #include "VJAdaptBoost.h"
  162. using namespace std;
  163. #define VERBOSE true
  164. //fail and messaging
  165. static void fail(const char* message){
  166. cerr << "Error:" << message << endl;
  167. exit(EXIT_FAILURE);
  168. }
  169. //order definition for this type of pairs
  170. //compare only the feature values
  171. static bool myPairOrder(
  172. const pair<float, int>& one
  173. , const pair<float, int>& other
  174. ){
  175. return one.first < other.first;
  176. }
  177. //why is one stump better than the other
  178. static bool myStumpOrder(
  179. const StumpRule & one
  180. , const StumpRule & other
  181. ){
  182. if(one.weightedError < other.weightedError)
  183. return true;
  184. if(one.weightedError == other.weightedError && one.margin > other.margin)
  185. return true;
  186. return false;
  187. }
  188. int AdaptBoost::getTrainingExampleIndex(int featureIndex, int iterator){
  189. assert(ascendingFeatures.size() > 0 && ascendingFeatures[0].size() >0);
  190. return ascendingFeatures[featureIndex][iterator].second;
  191. }
  192. float AdaptBoost::getTrainingExampleFeature(int featureIndex, int iterator){
  193. assert(ascendingFeatures.size() > 0 && ascendingFeatures[0].size() >0);
  194. if(_isnan(ascendingFeatures[featureIndex][iterator].first)){
  195. cerr<<"ERROR: nan feature "<<featureIndex<<" detected for example "<<getTrainingExampleIndex(featureIndex, iterator)<<endl;
  196. exit(EXIT_FAILURE);
  197. }
  198. return ascendingFeatures[featureIndex][iterator].first;
  199. }
  200. //constructor
  201. AdaptBoost::AdaptBoost(
  202. int positives
  203. , int negatives
  204. , long double positiveWeight
  205. , const vector< vector<float> >& data) {
  206. assert(positives > 0 && negatives > 0);
  207. assert(positiveWeight > 0 && positiveWeight < 1);
  208. assert(data.size() > 0 && data[0].size() > 0 );
  209. assert(data.size() == (positives + negatives));
  210. //add number of data info to features
  211. vector< vector<pair<float, int>> > features(data.size(), vector<pair<float, int>>(data[0].size(), pair<float, int>(0,0)));
  212. for(int i=0; i<features.size(); i++) {
  213. for(int j=0; j<features[0].size(); j++) {
  214. features[i][j] = pair<float, int>(data[i][j], i);
  215. }
  216. }
  217. //initialize the class attributes for the training set
  218. nPositives = positives;
  219. nNegatives = negatives;
  220. initialPositiveWeight = positiveWeight;
  221. sortFeatures(features);//initialize ascendingFeatures
  222. sampleCount = positives + negatives;
  223. featureCount = ascendingFeatures.size();
  224. positiveTotalWeight = positiveWeight;
  225. negativeTotalWeight = 1 - positiveWeight;
  226. long double posAverageWeight = positiveTotalWeight/(long double)nPositives;
  227. long double negAverageWeight = negativeTotalWeight/(long double)nNegatives;
  228. maxWeight = max(posAverageWeight, negAverageWeight);
  229. minWeight = min(posAverageWeight, negAverageWeight);
  230. exponentialRisk = 1;
  231. //set weights for each example
  232. for(int exampleIndex = 0; exampleIndex < sampleCount; exampleIndex++){
  233. weights.push_back(exampleIndex < nPositives ? posAverageWeight : negAverageWeight);
  234. labels.push_back(exampleIndex < nPositives ? 1 : -1);
  235. }
  236. }
  237. //destructor
  238. AdaptBoost::~AdaptBoost() {
  239. }
  240. //adaptBoost interface for training
  241. void AdaptBoost::adaptBoostTraining(int numOfWeakClassifier) {
  242. assert(numOfWeakClassifier > 0);
  243. for(int i=0; i<numOfWeakClassifier; i++) {
  244. oneRoundOfAdaboostTraining();
  245. }
  246. }
  247. //validation procedure using training examples
  248. vector<int> AdaptBoost::calcEmpiricalErrorInAdaBoostTraining(
  249. float tweak
  250. , float & falsePositive
  251. , float & detectionRate
  252. ){
  253. vector<int> blackList;
  254. blackList.resize(nPositives, 0);
  255. blackList.resize(nPositives+nNegatives, 1);
  256. int nFalsePositive = 0;
  257. int nFalseNegative = 0;
  258. //initially let all be positive
  259. vector<int> prediction;
  260. prediction.resize(sampleCount,0);
  261. predictLabelOfTrainingExamples(tweak, prediction, false);
  262. //evaluate prediction errors
  263. vector<int> agree(sampleCount);
  264. for(int i=0; i<sampleCount; i++) {
  265. agree[i] = labels[i]*prediction[i];
  266. }
  267. for(int exampleIndex = 0; exampleIndex < sampleCount; exampleIndex++){
  268. if(agree[exampleIndex] < 0) {
  269. if(exampleIndex < nPositives){
  270. nFalseNegative += 1;
  271. blackList[exampleIndex] = 1;
  272. }else{
  273. nFalsePositive += 1;
  274. blackList[exampleIndex] = 0;
  275. }
  276. }
  277. }
  278. //set the returned values
  279. falsePositive = nFalsePositive/(float)nNegatives;
  280. detectionRate = 1 - nFalseNegative/(float)nPositives;
  281. return blackList;
  282. }
  283. //given a tweak and a committe, what prediction does it make as to the training examples
  284. void AdaptBoost::predictLabelOfTrainingExamples(
  285. float tweakThreshold
  286. , vector<int> & prediction
  287. , bool onlyMostRecent
  288. ){
  289. int committeeSize = committee.size();
  290. //no need to weigh a single member's decision
  291. onlyMostRecent = committeeSize == 1 ? true : onlyMostRecent;
  292. int start = onlyMostRecent ? committeeSize - 1 : 0;
  293. //double to be more precise
  294. vector<vector<double>> memberVerdict;
  295. for(int i=0; i<committeeSize; i++) {//initialize memberVerdict
  296. vector<double> row(sampleCount);
  297. memberVerdict.push_back(row);
  298. }
  299. vector<double> memberWeight(committeeSize);
  300. //members, go ahead
  301. for(int member = start; member < committeeSize; member++){
  302. //sanity check
  303. if(committee[member].weightedError == 0 && member != 0)
  304. fail("Boosting Error Occured!");
  305. //0.5 does not count here
  306. //if member's weightedError is zero, member weight is nan, but it won't be used anyway
  307. memberWeight[member] = log(1./committee[member].weightedError -1);
  308. int feature = committee[member].featureIndex;
  309. #pragma omp parallel for schedule(static)
  310. for(int iterator = 0; iterator < sampleCount; iterator++){
  311. int exampleIndex = getTrainingExampleIndex(feature, iterator);
  312. memberVerdict[member][exampleIndex] = (getTrainingExampleFeature(feature, iterator) >
  313. committee[member].threshold ? 1 : -1)*committee[member].toggle + tweakThreshold;
  314. }
  315. }
  316. //joint session
  317. if(!onlyMostRecent){
  318. vector<double> finalVerdict(sampleCount);
  319. for(int i=0; i<sampleCount; i++) {
  320. double predict = 0;
  321. for(int j=0; j<committeeSize; j++) {
  322. predict += (memberWeight[j] * memberVerdict[j][i]);
  323. }
  324. finalVerdict[i] = predict;
  325. }
  326. for(int exampleIndex = 0; exampleIndex < sampleCount; exampleIndex++)
  327. prediction[exampleIndex] = finalVerdict[exampleIndex] > 0 ? 1 : -1;
  328. }else{
  329. for(int exampleIndex = 0; exampleIndex < sampleCount; exampleIndex++)
  330. prediction[exampleIndex] = memberVerdict[start][exampleIndex] > 0 ? 1 : -1;
  331. }
  332. }
  333. void AdaptBoost::oneRoundOfAdaboostTraining(){
  334. //try to be friendly here
  335. static int trainPhase = 0;
  336. if(VERBOSE && trainPhase == 0){
  337. cout << "\n#############################ADABOOST MESSAGE EXPLAINED####################################################\n\n";
  338. cout << "INFO: Adaboost starts. Exponential Risk is expected to go down steadily and strictly," << endl;
  339. cout << "INFO: and Exponential Risk should bound the (weighted) Empirical Error from above." << endl;
  340. cout << "INFO: Train Phase is the current boosting iteration." << endl;
  341. cout << "INFO: Best Feature is the most discriminative feature selected by decision stump at this iteration." << endl;
  342. cout << "INFO: Threshold and Toggle are two parameters that define a real valued decision stump.\n" << endl;
  343. }
  344. trainPhase++;
  345. //get and store the rule
  346. StumpRule rule = bestStump();
  347. committee.push_back(rule);
  348. //how it fares
  349. vector<int> prediction(sampleCount);
  350. predictLabelOfTrainingExamples(
  351. 0
  352. , prediction
  353. , /*onlyMostRecent*/ true);
  354. vector<bool> agree(sampleCount);
  355. for(int i=0; i<sampleCount; i++) {
  356. if(prediction[i] == labels[i]) {
  357. agree[i] = true;
  358. }else {
  359. agree[i] = false;
  360. }
  361. }
  362. //update weights
  363. vector<double> weightUpdate;
  364. weightUpdate.resize(sampleCount,1);
  365. bool errorFlag = false;
  366. for(int exampleIndex = 0; exampleIndex < sampleCount; exampleIndex++){
  367. //more weight for a difficult example
  368. if(!agree[exampleIndex]){
  369. weightUpdate[exampleIndex] = 1/rule.weightedError - 1;
  370. errorFlag = true;
  371. }
  372. }
  373. //update weights only if there is an error
  374. if(errorFlag){
  375. double weightSum = 0;
  376. for(int i=0; i<sampleCount; i++) {
  377. weights[i] *= weightUpdate[i];
  378. weightSum += weights[i];
  379. }
  380. for(int i=0; i<sampleCount; i++) {
  381. weights[i] /= weightSum;
  382. }
  383. double posTotalWeight = 0;
  384. for(int i=0; i<nPositives; i++) {
  385. posTotalWeight += weights[i];
  386. }
  387. positiveTotalWeight = posTotalWeight;
  388. negativeTotalWeight = 1-positiveTotalWeight;
  389. double min,max;
  390. min = max = weights[0];
  391. for(int i=0; i<sampleCount; i++) {
  392. if(weights[i] < min) {
  393. min = weights[i];
  394. }else if(weights[i] > max) {
  395. max = weights[i];
  396. }
  397. }
  398. minWeight = min;
  399. maxWeight = max;
  400. }
  401. //exponentialRisk can be zero at the first boosting
  402. exponentialRisk *= 2*sqrt((1-rule.weightedError)*rule.weightedError);
  403. //print some statistics
  404. if(VERBOSE){
  405. float tweak = 0;
  406. float falsePositive = 0;
  407. float detectionRate = 0;
  408. calcEmpiricalErrorInAdaBoostTraining(tweak, falsePositive, detectionRate);
  409. float empError = static_cast<float>(falsePositive*(1-initialPositiveWeight)+initialPositiveWeight*(1-detectionRate));
  410. cout << "Training Performance Explanation (before threshold tweaking): falsePositive " << falsePositive
  411. << " detectionRate " << detectionRate << endl;
  412. cout <<"###########################################################################################################\n";
  413. cout << "\nTrain Phase " << trainPhase << endl << endl;
  414. // whatFeature(rule.featureIndex);
  415. cout << "\tExponential Risk " << setw(12) << exponentialRisk << setw(19) << "Weighted Error "
  416. << setw(11) << rule.weightedError << setw(14) << "Threshold " << setw(10) << rule.threshold
  417. << setw(13) <<"Toggle " << setw(12) << rule.toggle << endl;
  418. cout << "\tPositive Weight" << setw(14) << positiveTotalWeight << setw(14) << "MinWeight "
  419. << setw(16) << minWeight << setw(14) << "MaxWeight " << setw(10) << maxWeight << setw(22)
  420. << "Empirical Error " << setw(10) << empError << endl << endl;
  421. }
  422. }
  423. //get a feature from features and put them in ascending order
  424. //and record at the same time the permuted example order
  425. void AdaptBoost::sortFeatures(vector< vector<pair<float, int>> >& features) {
  426. assert(features.size()!=0 && features[0].size() !=0 );
  427. for(unsigned int i=0; i<features[0].size(); i++) {
  428. vector<pair<float, int>> temp = vector<pair<float, int>>();
  429. for(unsigned int j=0; j<features.size(); j++) {
  430. temp.push_back(features[j][i]);
  431. }
  432. //sort
  433. sort(temp.begin(), temp.end(), myPairOrder);
  434. ascendingFeatures.push_back(temp);
  435. }
  436. }
  437. //base learner is a stump, a decision tree of depth 1
  438. //decisionStump has to look at feature and return rule
  439. void AdaptBoost::decisionStump(
  440. int featureIndex
  441. , StumpRule & best
  442. ){
  443. //a stump is determined by threshold and toggle, the other two attributes measures its performance
  444. //initialize with some crazy values
  445. best.featureIndex = featureIndex;
  446. best.weightedError = 2;
  447. best.threshold = getTrainingExampleFeature(featureIndex, 0) - 1;
  448. best.margin = -1;
  449. best.toggle = 0;
  450. StumpRule current = best;
  451. //error_p and error_n allow to set the best toggle
  452. long double error_p, error_n;
  453. //initialize: r denotes right hand side and l left hand side
  454. //convention: in TrainExamples nPositives positive samples are followed by negatives samples
  455. long double rPositiveWeight = positiveTotalWeight;
  456. long double rNegativeWeight = negativeTotalWeight;
  457. //yes, nothing to the left of the sample with the smallest feature
  458. long double lPositiveWeight = 0;
  459. long double lNegativeWeight = 0;
  460. //go through all these observations one after another
  461. int iterator = -1;
  462. //to build a decision stump, you need a toggle and an admissible threshold
  463. //which doesn't coincide with any of the observations
  464. while(true){
  465. //We've got a threshold. So determine the best toggle based on two types of error
  466. //toggle = 1, positive prediction if and only if the observed feature > the threshold
  467. //toggle = -1, positive prediction if and only if the observed feature < the threshold
  468. //error_p denotes the error introduced by toggle = 1, error_n the error by toggle = -1
  469. error_p = rNegativeWeight + lPositiveWeight;
  470. error_n = rPositiveWeight + lNegativeWeight;
  471. current.toggle = error_p < error_n ? 1 : -1;
  472. //sometimes shit happens, prevent error from being negative
  473. long double smallerError = min(error_p, error_n);
  474. //this prevents some spurious nonzero: for currentError must be at least equal to minWeight
  475. current.weightedError = smallerError < minWeight * 0.9 ? 0 : smallerError;
  476. //update if necessary
  477. if(myStumpOrder(current, best))
  478. best = current;
  479. //move on
  480. iterator++;
  481. //we don't actually need to look at the sample with the largest feature
  482. //because its rule is exactly equivalent to those produced
  483. //by the sample with the smallest feature on training observations
  484. //but it won't do any harm anyway
  485. if(iterator == sampleCount)
  486. break;
  487. //handle duplicates, update lr weights and find a new threshold
  488. while(true){
  489. //take this guy's attributes
  490. int exampleIndex = getTrainingExampleIndex(featureIndex, iterator);
  491. int label = labels[exampleIndex];
  492. long double weight = weights[exampleIndex];
  493. //update weights
  494. if(label < 0){
  495. lNegativeWeight += weight;
  496. rNegativeWeight -= weight;
  497. }else{
  498. lPositiveWeight += weight;
  499. rPositiveWeight -= weight;
  500. }
  501. //if a new threshold can be found, break
  502. //two cases are possible: either it is the last observation
  503. if(iterator == sampleCount - 1)
  504. break;
  505. //or no duplicate. If there is a duplicate, repeat
  506. if(getTrainingExampleFeature(featureIndex, iterator) != getTrainingExampleFeature(featureIndex, iterator + 1)){
  507. double test = ((double)getTrainingExampleFeature(featureIndex, iterator)
  508. + (double)getTrainingExampleFeature(featureIndex, iterator + 1))/2;
  509. //well that's a bit frustrating: I want to keep float because of memory constraint, but apparently
  510. //features are so close, sometimes, numerical precision arises as an unexpected problem, so I decide
  511. //to use a double threshold so as to separate float features
  512. if(getTrainingExampleFeature(featureIndex, iterator) < test && test < getTrainingExampleFeature(featureIndex, iterator + 1))
  513. break;
  514. else{
  515. #pragma omp critical
  516. {
  517. cout << "ERROR: numerical precision breached: problem feature values "
  518. << getTrainingExampleFeature(featureIndex, iterator)
  519. << " : " << getTrainingExampleFeature(featureIndex, iterator+1)
  520. << ". Problem feature " << featureIndex << " and problem example "
  521. << getTrainingExampleIndex(featureIndex, iterator) << " : "
  522. << getTrainingExampleIndex(featureIndex, iterator+1) << endl;
  523. }
  524. fail("fail to find a suitable threshold.");
  525. }
  526. }
  527. iterator++;
  528. }
  529. //update threshold
  530. if(iterator < sampleCount - 1){
  531. current.threshold = ((double)getTrainingExampleFeature(featureIndex, iterator)
  532. + (double)getTrainingExampleFeature(featureIndex, iterator + 1))/2;
  533. current.margin = getTrainingExampleFeature(featureIndex, iterator + 1) - getTrainingExampleFeature(featureIndex, iterator);
  534. }else{
  535. //slightly to the right of the biggest observation
  536. current.threshold = getTrainingExampleFeature(featureIndex, iterator) + 1;
  537. current.margin = 0;
  538. }
  539. }
  540. }
  541. //implement the feature selection's outer loop
  542. //return the most discriminative feature and its rule
  543. StumpRule AdaptBoost::bestStump(
  544. ){
  545. vector<StumpRule> candidates;
  546. candidates.resize(featureCount);
  547. #pragma omp parallel for schedule(static)
  548. for(int featureIndex = 0; featureIndex < featureCount; featureIndex++)
  549. decisionStump(featureIndex, candidates[featureIndex]);
  550. //loop over all the features
  551. //the best rule has the smallest weighted error and the largest margin
  552. StumpRule best = candidates[0];
  553. for(int featureIndex = 1; featureIndex < featureCount; featureIndex++){
  554. if(myStumpOrder(candidates[featureIndex], best))
  555. best = candidates[featureIndex];
  556. }
  557. //if shit happens, tell me
  558. if( best.weightedError >= 0.5 )
  559. fail("Decision Stump failed: base error >= 0.5");
  560. //return
  561. return best;
  562. }

reference:

Yi-Qing Wang, An Analysis of the Viola-Jones Face Detection Algorithm, IPOL.

转载于:https://blog.51cto.com/remyspot/1638356

发表评论

表情:
评论列表 (有 0 条评论,357人围观)

还没有评论,来说两句吧...

相关阅读

    相关 人脸特征检测--基于DLIB库

      Dlib是一个C++编写的工具包,它包含了机器学习算法以及一些用来解决现实复杂问题的工具,可以广泛应用于机器人、嵌入式设备、手机,甚至高性能计算中,可以在其官网了解更多。这

    相关 特征选择_过滤特征选择

    一:方差选择法: 使用方差作为特征评分标准,如果某个特征的取值差异不大,通常认为该特征对区分样本的贡献度不大 因此在构造特征过程中去掉方差小于阈值特征 f