├── 620model ├── 1226model ├── train ├── demo ├── TrainDetector.hpp ├── makefile.example ├── demo.cpp ├── TrainDetector.cpp ├── common.cpp ├── common.hpp ├── LearnDQT.hpp ├── data.hpp ├── LearnGAB.hpp ├── data.cpp ├── LearnDQT.cpp └── LearnGAB.cpp ├── detection ├── 1.jpg ├── 2.jpg ├── common.cpp ├── makefile.example ├── TrainDetector.hpp ├── demo.cpp ├── common.hpp ├── TrainDetector.cpp ├── LearnGAB.hpp └── LearnGAB.cpp ├── discROC-compare.png ├── LICENSE └── README.md /620model: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wincle/NPD/HEAD/620model -------------------------------------------------------------------------------- /1226model: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wincle/NPD/HEAD/1226model -------------------------------------------------------------------------------- /train/demo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wincle/NPD/HEAD/train/demo -------------------------------------------------------------------------------- /detection/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wincle/NPD/HEAD/detection/1.jpg -------------------------------------------------------------------------------- /detection/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wincle/NPD/HEAD/detection/2.jpg -------------------------------------------------------------------------------- /discROC-compare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wincle/NPD/HEAD/discROC-compare.png -------------------------------------------------------------------------------- /detection/common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | 3 | Options::Options(){ //default value 4 | objSize = 24; 5 | model_dir= "../620model"; 6 | fddb_dir = "../data/fddb"; 7 | enDelta = 0.1; 8 | } 9 | -------------------------------------------------------------------------------- /detection/makefile.example: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | OPENCV = -L/local/lib/ -lopencv_highgui -lopencv_core -lopencv_imgproc -I/local/include/opencv 3 | all : demo 4 | demo: common.cpp common.hpp TrainDetector.cpp TrainDetector.hpp LearnGAB.cpp LearnGAB.hpp 5 | $(CC) demo.cpp common.cpp TrainDetector.cpp LearnGAB.cpp $(OPENCV) -fopenmp -o demo -O3 -DNDEBUG 6 | -------------------------------------------------------------------------------- /train/TrainDetector.hpp: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | #include "data.hpp" 3 | #include 4 | /* \breif Wraper for call Detector */ 5 | class TrainDetector{ 6 | public: 7 | /* 8 | * \breif Training 9 | * Load the dataset first and load model if exit, 10 | * Training Detector next 11 | */ 12 | void Train(); 13 | }; 14 | -------------------------------------------------------------------------------- /train/makefile.example: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | OPENCV = -L/local/lib/ -lopencv_highgui -lopencv_core -lopencv_imgproc -I/local/include/opencv 3 | all : demo 4 | demo: common.cpp common.hpp TrainDetector.cpp TrainDetector.hpp data.hpp data.cpp LearnGAB.cpp LearnGAB.hpp LearnDQT.cpp LearnDQT.hpp 5 | $(CC) demo.cpp common.cpp data.cpp TrainDetector.cpp LearnGAB.cpp LearnDQT.cpp $(OPENCV) -fopenmp -o demo -O3 -DNDEBUG 6 | -------------------------------------------------------------------------------- /detection/TrainDetector.hpp: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | #include 3 | /* \breif Wraper for call Detector */ 4 | class TrainDetector{ 5 | public: 6 | /* 7 | * \breif single detect 8 | */ 9 | void Detect(); 10 | /* 11 | * \breif Detect For FDDB 12 | */ 13 | void FddbDetect(); 14 | /* 15 | * \breif Detect face from camera 16 | */ 17 | void Live(); 18 | }; 19 | -------------------------------------------------------------------------------- /train/demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "TrainDetector.hpp" 3 | using namespace std; 4 | 5 | /*! \breif command help */ 6 | static const char help[] = "NPD\n\n" 7 | "train: train a model ,if you already have, will resume it\n"; 8 | 9 | /*! 10 | * \breif Command Dispatch 11 | */ 12 | int main(int argc, char* argv[]){ 13 | TrainDetector dector; 14 | if (argc != 2) { 15 | printf(help); 16 | } 17 | else if (strcmp(argv[1], "train") == 0) { 18 | dector.Train(); 19 | } 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /train/TrainDetector.cpp: -------------------------------------------------------------------------------- 1 | #include "TrainDetector.hpp" 2 | #include "LearnGAB.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace cv; 9 | 10 | void TrainDetector::Train(){ 11 | Options& opt = Options::GetInstance(); 12 | DataSet pos,neg; 13 | 14 | GAB Gab; 15 | Gab.LoadModel(opt.outFile); 16 | DataSet::LoadDataSet(pos, neg, Gab.stages); 17 | Gab.LearnGAB(pos,neg); 18 | Gab.Save(); 19 | pos.Clear(); 20 | neg.Clear(); 21 | } 22 | -------------------------------------------------------------------------------- /train/common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | 3 | Options::Options(){ //default value 4 | objSize = 24; 5 | treeLevel = 8; 6 | maxNumWeaks = 1000; 7 | numThreads = 16; 8 | minDR = 1.0; 9 | maxFAR = 0; 10 | faceDBFile = "../data/FaceDB.txt"; 11 | nonfaceDBFile = "../data/NonfaceDB.txt"; 12 | outFile = "../result"; 13 | fddb_dir = "../data/fddb"; 14 | tmpfile = "../data/tmpFaceDB.txt"; 15 | initNeg = "../data/hd.txt"; 16 | trimFrac = 0.005; 17 | minLeafFrac = 0.01; 18 | minLeaf = 100; 19 | minSamples = 20000; 20 | maxWeight = 100; 21 | augment = true; 22 | saveStep = 10; 23 | generate_hd = false; 24 | enDelta = 0.1; 25 | useInitHard = false; 26 | negRatio = 0.5; 27 | } 28 | -------------------------------------------------------------------------------- /detection/demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "TrainDetector.hpp" 3 | using namespace std; 4 | 5 | /*! \breif command help */ 6 | static const char help[] = "NPD\n" 7 | "test: test one image\n" 8 | "fddb: test FDDB data\n" 9 | "live: live demo with camera support\n"; 10 | 11 | /*! 12 | * \breif Command Dispatch 13 | */ 14 | int main(int argc, char* argv[]){ 15 | TrainDetector dector; 16 | if (argc != 2) { 17 | printf(help); 18 | } 19 | else if (strcmp(argv[1], "test") == 0) { 20 | dector.Detect(); 21 | } 22 | else if (strcmp(argv[1], "fddb") == 0) { 23 | dector.FddbDetect(); 24 | } 25 | else if (strcmp(argv[1], "live") == 0) { 26 | dector.Live(); 27 | } 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /detection/common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_HPP_ 2 | #define COMMON_HPP_ 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | /* 8 | * \breif Configure of NPD 9 | */ 10 | class Options{ 11 | public: 12 | static inline Options& GetInstance() { 13 | static Options opt; 14 | return opt; 15 | } 16 | /* \breif Size of Template */ 17 | int objSize; 18 | /* \breif model path */ 19 | string model_dir; 20 | /* \breif path of FDDB */ 21 | string fddb_dir; 22 | /* \breif use for resize box */ 23 | float enDelta; 24 | 25 | private: 26 | Options(); 27 | Options(const Options& other); 28 | Options& operator=(const Options& other); 29 | 30 | }; 31 | #endif // COMMON_HPP_ 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, wangchao 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of NPD nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NPD 2 | 3 | The C++ implementation of A Fast and Accurate Unconstrained Face Detector. 4 | 5 | The result is trained by 200k pos data and the template is 24*24, stages number is 620, model size is 540kb. 6 | 7 | minFaceSize | speed(ms) | cores 8 | :-----: | :----: | :----: 9 | 80*80 | 30 | 1 10 | 24x24 | 500 | 1 11 | 24*24 | 60 | 16 12 | 13 | the detection result is test on FDDB data set (average 400*400) 14 | 15 | # NOTICE 16 | 17 | The "1226model" is dump from matlab code which is from References, this model has 1226 stages. 18 | 19 | You must change the code in detection/LearnGAB.cpp:58-64. Because the difference between matlab and OpenCV. You should also change the coefficient in detection/LearnGAB.cpp:262-265 to fit the model. 20 | 21 | # How to use 22 | - you should mkdir data first 23 | 24 | In data folder, you should creat two file named FaceDB.txt and NonFaceDB.txt. 25 | 26 | ``` 27 | FaceDB.txt 28 | ../data/face/00001.jpg x1 y1 x2 y2 29 | ../data/face/00002.jpg x1 y1 x2 y2 30 | .... 31 | .... 32 | ``` 33 | 34 | ``` 35 | NonfaceDB.txt 36 | ../data/bg/000001.jpg 37 | ../data/bg/000002.jpg 38 | ../data/bg/000003.jpg 39 | .... 40 | .... 41 | ``` 42 | 43 | ``` 44 | hd.txt(Optional) 45 | ../data/hd/000001.jpg 46 | ../data/hd/000002.jpg 47 | ../data/hd/000003.jpg 48 | ... 49 | ``` 50 | 51 | the hd image is hard negative for init training , the size of it should to be the same with your model template(24 for me). 52 | 53 | The config is in common.cpp 54 | 55 | # License 56 | 57 | BSD 3-Clause 58 | 59 | # Python Implemention 60 | 61 | https://github.com/wincle/NPD_python 62 | 63 | (It's much slow than C++ implementisn. PR is welcomed) 64 | 65 | # References 66 | 67 | http://www.cbsr.ia.ac.cn/users/scliao/projects/npdface/index.html 68 | -------------------------------------------------------------------------------- /train/common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_HPP_ 2 | #define COMMON_HPP_ 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | /* 8 | * \breif Configure of NPD 9 | */ 10 | class Options{ 11 | public: 12 | static inline Options& GetInstance() { 13 | static Options opt; 14 | return opt; 15 | } 16 | /* \breif Size of Template */ 17 | int objSize; 18 | /* \breif a text file for positive dataset */ 19 | string faceDBFile; 20 | /* \breif a text file for negative dataset */ 21 | string nonfaceDBFile; 22 | /* \breif path of model */ 23 | string outFile; 24 | /* \breif path of FDDB */ 25 | string fddb_dir; 26 | /* \breif a text file for resume training status */ 27 | string tmpfile; 28 | /* \breif Init Neg Samples */ 29 | string initNeg; 30 | /* \breif depth of a stage */ 31 | int treeLevel; 32 | /* \breif max number of stages */ 33 | int maxNumWeaks; 34 | /* \breif threads to use */ 35 | int numThreads; 36 | /* \breif recall of positive in every stages */ 37 | double minDR; 38 | /* \breif end condition of the training */ 39 | double maxFAR; 40 | /* \breif max value of weight */ 41 | int maxWeight; 42 | /* \breif factor for decide leaf number */ 43 | double minLeafFrac; 44 | /* \breif minimum leaf number */ 45 | int minLeaf; 46 | /* \breif factor to decide how many samples should be filter befor training a stage */ 47 | double trimFrac; 48 | /* \breif minimum samples required */ 49 | int minSamples; 50 | /* \breif data augment or not */ 51 | bool augment; 52 | /* \breif step of stages to save the model */ 53 | int saveStep; 54 | /* \breif generate init neg if need */ 55 | bool generate_hd; 56 | /* \breif use for resize box */ 57 | float enDelta; 58 | /* \use hd or not */ 59 | bool useInitHard; 60 | /* \Ration of neg/pos */ 61 | float negRatio; 62 | 63 | private: 64 | Options(); 65 | Options(const Options& other); 66 | Options& operator=(const Options& other); 67 | 68 | }; 69 | #endif // COMMON_HPP_ 70 | -------------------------------------------------------------------------------- /detection/TrainDetector.cpp: -------------------------------------------------------------------------------- 1 | #include "TrainDetector.hpp" 2 | #include "LearnGAB.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace cv; 9 | 10 | void TrainDetector::Detect(){ 11 | Options& opt = Options::GetInstance(); 12 | 13 | GAB Gab; 14 | Gab.LoadModel(opt.model_dir); 15 | 16 | timeval start, end; 17 | float time = 0; 18 | 19 | string path = "1.jpg"; 20 | Mat img = imread(path, CV_LOAD_IMAGE_GRAYSCALE); 21 | vector rects; 22 | vector scores; 23 | vector index; 24 | gettimeofday(&start,NULL); 25 | index = Gab.DetectFace(img,rects,scores); 26 | gettimeofday(&end,NULL); 27 | float t = 1000 * (end.tv_sec-start.tv_sec)+ (end.tv_usec-start.tv_usec)/1000; 28 | printf("use time:%f\n",t); 29 | for(int i = 0;i < index.size(); i++){ 30 | printf("%d %d %d %d %lf\n", rects[index[i]].x, rects[index[i]].y, rects[index[i]].width, rects[index[i]].height, scores[index[i]]); 31 | for (int i = 0; i < index.size(); i++) { 32 | if(scores[index[i]]>0) 33 | img = Gab.Draw(img, rects[index[i]]); 34 | } 35 | imwrite("2.jpg",img); 36 | } 37 | } 38 | 39 | 40 | void TrainDetector::FddbDetect(){ 41 | Options& opt = Options::GetInstance(); 42 | 43 | const char* fddb_dir=opt.fddb_dir.c_str(); 44 | string prefix = opt.fddb_dir + string("/"); 45 | GAB Gab; 46 | Gab.LoadModel(opt.model_dir); 47 | 48 | timeval start, end; 49 | float time = 0; 50 | 51 | for(int i = 1;i<=10;i++){ 52 | char fddb[300]; 53 | char fddb_out[300]; 54 | sprintf(fddb, "%s/FDDB-folds/FDDB-fold-%02d.txt", fddb_dir, i); 55 | sprintf(fddb_out, "%s/result/fold-%02d-out.txt", fddb_dir, i); 56 | FILE* fin = fopen(fddb, "r"); 57 | FILE* fout = fopen(fddb_out, "w"); 58 | char path[300]; 59 | 60 | while (fscanf(fin, "%s", path) > 0) { 61 | string full_path = prefix + string(path) + string(".jpg"); 62 | Mat img = imread(full_path, CV_LOAD_IMAGE_GRAYSCALE); 63 | vector rects; 64 | vector scores; 65 | vector index; 66 | gettimeofday(&start,NULL); 67 | index = Gab.DetectFace(img,rects,scores); 68 | gettimeofday(&end,NULL); 69 | float t = 1000 * (end.tv_sec-start.tv_sec)+ (end.tv_usec-start.tv_usec)/1000; 70 | time += t; 71 | printf("%s\n%d\n",path,index.size()); 72 | printf("use time:%f\n",t); 73 | fprintf(fout,"%s\n%d\n",path,index.size()); 74 | for(int i = 0;i < index.size(); i++){ 75 | printf("%d %d %d %d %lf\n", rects[index[i]].x, rects[index[i]].y, rects[index[i]].width, rects[index[i]].height, scores[index[i]]); 76 | fprintf(fout, "%d %d %d %d %lf\n", rects[index[i]].x, rects[index[i]].y, rects[index[i]].width, rects[index[i]].height, scores[index[i]]); 77 | } 78 | } 79 | printf("all time:%f\n",time); 80 | fclose(fin); 81 | fclose(fout); 82 | } 83 | } 84 | 85 | void TrainDetector::Live() { 86 | Options& opt = Options::GetInstance(); 87 | 88 | VideoCapture cap(0); 89 | if (!cap.isOpened()) { 90 | printf("Can not open Camera, Please Check it!"); 91 | return; 92 | } 93 | 94 | GAB Gab; 95 | Gab.LoadModel(opt.model_dir); 96 | 97 | while (true) { 98 | Mat frame; 99 | Mat gray; 100 | cap >> frame; 101 | 102 | cvtColor(frame, gray, CV_BGR2GRAY); 103 | vector rects; 104 | vector scores; 105 | vector index; 106 | index = Gab.DetectFace(gray,rects,scores); 107 | 108 | for (int i = 0; i < index.size(); i++) { 109 | if(scores[index[i]]>100) 110 | frame = Gab.Draw(frame, rects[index[i]]); 111 | } 112 | cv::imshow("live", frame); 113 | int key = cv::waitKey(30); 114 | if (key == 27) { 115 | break; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /train/LearnDQT.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _LEARNDQT_HPP 2 | #define _LEARNDQT_HPP 3 | #include "common.hpp" 4 | #include "data.hpp" 5 | #include 6 | #include 7 | /* 8 | * \breif Stage Training Process 9 | */ 10 | class DQT{ 11 | public: 12 | /* \breif feature-pixel map used for locate pixed location by featureId */ 13 | vector lpoints; 14 | vector rpoints; 15 | /* \breif A feature map used for speed up calculate feature */ 16 | cv::Mat ppNpdTable; 17 | 18 | public: 19 | /* 20 | * \breif Init maps 21 | */ 22 | DQT(); 23 | /* 24 | * \breif Main Preocess Wraper 25 | * 26 | * \param PosX positive samples pixel information 27 | * \param NegX negative samples pixel information 28 | * \param pPosW positive samples weights 29 | * \param pNegW negative samples weights 30 | * \param posIndex positive samples index 31 | * \param negIndex negative samples index 32 | * \param minLeaf minimum leaf number 33 | * \param feaId features learned by this stage 34 | * \param leftChild tree structure learned by this stage 35 | * \param rightChild tree structure learned by this stage 36 | * \param cutpoint double thresholds learned by this stage 37 | * \param fit score learned by this stage 38 | */ 39 | float Learn(cv::Mat posX,cv::Mat negX, float pPosW[], float pNegW[], vector posIndex,vector negIndex, int minLeaf, vector &feaId, vector &leftChild, vector &rightChild, vector< vector > &cutpoint, vector &fit); 40 | /* 41 | * \breif Learn one node in the tree 42 | * 43 | * \param posX ... 44 | * \param negX ... 45 | * \param posW ... 46 | * \param negW ... 47 | * \param posIndex ... 48 | * \param negIndex ... 49 | * \param nPos number of positive samples 50 | * \param nNeg number of negative samples 51 | * \param minLeaf ... 52 | * \param numThreads openmp threads number 53 | * \param parentFit parent score of the tree(default:0) 54 | * \param feaId feature learned by this node 55 | * \param cutpoint double thresholds learned by this node 56 | * \param fit score learned by this node 57 | */ 58 | float LearnQuadStump(vector &posX, vector &negX, float *posW, float *negW, int *posIndex, int *negIndex, int nPos, int nNeg, int minLeaf, int numThreads, float parentFit, int &feaId, unsigned char (&cutpoint)[2], float (&fit)[2]); 59 | /* 60 | * \breif Main Learning Process of a tree 61 | * \param posX ... 62 | * \param negX ... 63 | * \param posW ... 64 | * \param negW ... 65 | * \param posIndex ... 66 | * \param negIndex ... 67 | * \param nPos ... 68 | * \param nNeg ... 69 | * \param treeLevel max tree depths 70 | * \param minLeaf ... 71 | * \param numThreads ... 72 | * \param parentFit ... 73 | * \param feaId ... 74 | * \param cutpoint ... 75 | * \param fit ... 76 | */ 77 | float LearnDQT(vector &posX, vector &negX, float *posW, float *negW, int *posIndex, int *negIndex, int nPos, int nNeg, int treeLevel, int minLeaf, int numThreads, float parentFit, vector &feaId, vector< vector > &cutpoint, vector &leftChild, vector &rightChild, vector &fit); 78 | /* 79 | * \breif Count Weights by features 80 | * 81 | * \param X samples pixel at point A 82 | * \param Y samples pixel at point B 83 | * \param index index of samples 84 | * \param n samples number 85 | * \param count count pixel feature in this 86 | * \param wHist count weights in this 87 | */ 88 | void WeightHist(unsigned char *X, unsigned char *Y, float *W, int *index, int n, int count[256], float wHist[256]); 89 | /* 90 | * \breif Get pixel location by feature Id 91 | * 92 | * \param feaid feature Id 93 | * \param x point A location 94 | * \param y point B location 95 | */ 96 | void GetPoints(int feaid, int *x, int *y); 97 | }; 98 | #endif 99 | -------------------------------------------------------------------------------- /detection/LearnGAB.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _LEARNGAB_HPP 2 | #define _LEARNGAB_HPP 3 | #include "common.hpp" 4 | #include 5 | 6 | /* 7 | * breif The Detector for face classification 8 | */ 9 | class GAB{ 10 | public: 11 | /* 12 | * \breif Init Feature map 13 | * Not only generate feature map and also generate feature-coordinate map 14 | */ 15 | GAB(); 16 | /* 17 | * \breif Validate the region is a face or not 18 | * Go throw all the stages and accumulate the scores, 19 | * only if the score passed all the threshold, judge it to be a face 20 | * 21 | * param test the region to be test 22 | * param score the score finally it got 23 | * param sIndex the index of winSize 24 | */ 25 | bool NPDClassify(cv::Mat test,float &score,int sIndex); 26 | /* 27 | * \breif Get the coordinates by feature id 28 | * the feature number is calculate by (objSize*objSize)*(objSize*objSize-1)/2 29 | * so if you have a feature id, use this function to get the coordinates 30 | * got the coordinates you can calculate the feature value in image. 31 | * here use two maps which store feature-coordinates 32 | * 33 | * \param feaid Feature Id 34 | * \param x1 coordinate of point A.x 35 | * \param x2 coordinate of point A.y 36 | * \param y1 coordinate of point B.x 37 | * \param y2 coordinate of point B.y 38 | */ 39 | void GetPoints(int feaid, int *x1, int *y1, int *x2, int *y2); 40 | /* 41 | * \breif Mining Negative Samples 42 | * Use NextImage to get regions and than use NPDClassify to validate it's a face or not 43 | * Using a Mining rate to control travel speed 44 | * Using region_pool for openmp 45 | * 46 | * \param n the negative size final condition 47 | * \param neg negative dataset 48 | */ 49 | void LoadModel(string path); 50 | /* 51 | * \breif Draw rect in a image 52 | * 53 | * \param img the image need to be draw 54 | * \param rects the box 55 | */ 56 | cv::Mat Draw(cv::Mat& img, cv::Rect& rects); 57 | public: 58 | /* \breif indicate how many stages the dector have */ 59 | int stages; 60 | /* \breif vectors contain the model */ 61 | vector treeIndex; 62 | vector feaIds, leftChilds, rightChilds; 63 | vector cutpoints; 64 | vector fits; 65 | vector thresholds; 66 | int numBranchNodes; 67 | /* \breif save the points of feature id */ 68 | vector< vector > points1,points2; 69 | /* \breif vector contain point-feature map */ 70 | vector lpoints; 71 | vector rpoints; 72 | /* \breif A feature map used for speed up calculate feature */ 73 | cv::Mat ppNpdTable; 74 | public: 75 | /* \breif model template size */ 76 | int DetectSize; 77 | /* 78 | * \breif wraper for Detect faces from a image 79 | * Sliding and resize window to scrach all the regions 80 | * return a vector which save the index of face regions 81 | * 82 | * /param img The image need to be detected 83 | * /param rects The vector that contain the location of faces 84 | * /param scores the vector thar contain the faces score 85 | */ 86 | vector DetectFace(cv::Mat img,vector& rects, vector& scores); 87 | /* 88 | * \breif nms Non-maximum suppression 89 | * the Nms algorithm result concerned score of areas 90 | * 91 | * \param rects area of faces 92 | * \param scores score of faces 93 | * \param Srect size of rects 94 | * \param overlap overlap threshold 95 | * \param img get size of origin img 96 | * \return picked index 97 | */ 98 | vector Nms(vector& rects, vector& scores, vector& Srect, float overlap, cv::Mat img); 99 | /* 100 | * \breif function for Partation areas 101 | * From Predicate mat get a paration result 102 | * 103 | * \param predicate The matrix marked cross areas 104 | * \param label The vector marked classification label 105 | * return number of classfication 106 | */ 107 | int Partation(cv::Mat_& predicate,vector& label); 108 | /* 109 | * \breif Find classfication area parent 110 | * 111 | * \param parent parent vector 112 | * \param x current node 113 | */ 114 | int Find(vector& parent,int x); 115 | /* 116 | * \breif Compute score 117 | * y = log(1+exp(x)); 118 | * 119 | * \param scores score vector 120 | * \param index score index 121 | */ 122 | vector Logistic(vector scores ,vector index); 123 | }; 124 | #endif 125 | -------------------------------------------------------------------------------- /train/data.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _DATA_HPP 2 | #define _DATA_HPP 3 | #include "common.hpp" 4 | #include 5 | #include 6 | 7 | /*! 8 | * \breif DataSet Wrapper 9 | */ 10 | class DataSet { 11 | public: 12 | DataSet(); 13 | /* 14 | * \breif Load Wrapper for `LoadPositiveDataSet` and `LoadNegative DataSet` 15 | * Since positive dataset and negative dataset may share some information between 16 | * each other, we need to load them all together 17 | * 18 | * \param stages indicate if it is resume. 19 | */ 20 | static void LoadDataSet(DataSet& pos, DataSet& neg, int stages); 21 | /* 22 | * \breif Load Postive DataSet 23 | * All positive samples are listed in this text file with each line represents a sample. 24 | * dataset could be augment in this stage, it include minor, resize and shift. 25 | * finally it will be Produce ten times DataSet than origin. 26 | * 27 | * \param positive a text file path 28 | * \param stages indicate if it is resume 29 | */ 30 | void LoadPositiveDataSet(const std::string& positive, int stages); 31 | /* 32 | * \breif Load Negative DataSet 33 | * We generate negative samples like positive samples before the program runs. Each line 34 | * of the text file hold another text file which holds the real negative sample path in 35 | * the filesystem, all the image's path will be saved and imgs will be generated in 36 | * these images. 37 | * 38 | * \param negative a text file path 39 | * \param pos_num the size of negative imgs should be 40 | * \param stages indicate if it is resume 41 | */ 42 | void LoadNegativeDataSet(const std::string& negative,const int pos_num,int stages); 43 | /* 44 | * \breif Generate a img for negative samples 45 | * Will scrach a negative samples from image which lived in list 46 | * the x,y and width,height of sample is random, it will be resized 47 | * finally will flip or minor the sample by random 48 | * 49 | * \param i the index of image in list and seed for random 50 | */ 51 | cv::Mat NextImage(int seed); 52 | /* 53 | * \breif Generate images for negative samples 54 | * Generate a pool to generate negative sample, in order to call NextImage in parallel 55 | * 56 | * param n the number of samples needed 57 | */ 58 | void MoreNeg(int n); 59 | /* 60 | * \breif Remove Images from negative samples 61 | * It delete images in the vector(imgs) and clear scores(Fx) 62 | * finally update the size 63 | * 64 | * param PassIndex the index of images which should be keeped 65 | */ 66 | void Remove(vector PassIndex); 67 | /* 68 | * \breif Release the memory of W and Fx which are created by new 69 | */ 70 | void ImgClear(); 71 | /* 72 | * \breif Init weights and scores 73 | */ 74 | void initWeights(); 75 | /* 76 | * \breif Extract Imgs Information 77 | * Extract images from vector to a Mat 78 | * the rows should be the Pixel amount, the cols should be the images amount 79 | */ 80 | cv::Mat ExtractPixel(); 81 | /* 82 | * \breif Update Weight 83 | * Init Weight to 1/size 84 | * Calculate by exp(-y*Fx) 85 | * 86 | * param y flag of dataset, pos is 1, neg is -1 87 | * param maxWeight the upper limit of weights 88 | */ 89 | void CalcWeight(int y, int maxWeight); 90 | /* 91 | * \breif Release the memory of vector imgs 92 | */ 93 | void Clear(); 94 | public: 95 | /* \breif samples for training */ 96 | std::vector imgs; 97 | /* \breif number of samples */ 98 | int size; 99 | /* \breif number of pixel in a sample */ 100 | int numPixels; 101 | /* \breif number of features contrain in a sample */ 102 | int feaDims; 103 | /* \breif Weights of samples */ 104 | float *W; 105 | /* \breif Scores of samples */ 106 | float *Fx; 107 | public: 108 | /* \breif stored paths of negative images */ 109 | std::vector list; 110 | /* \breif image pool for generate negative samples */ 111 | std::vector NegImgs; 112 | /* \breif array of current image to generate negative samples , 113 | * set the size to be your cores num */ 114 | int current_id[16]; 115 | /* \breif array of location for travel negative images */ 116 | int x[16]; 117 | int y[16]; 118 | /* \breif array of factors for resize negative images when traveling negative images */ 119 | float factor[16]; 120 | /* \breif array of step when traveling negative images */ 121 | int step[16]; 122 | /* \breif array of flip type when traveling negative images */ 123 | int tranType[16]; 124 | /* \breif array of window size when traveling negative images */ 125 | int win[16]; 126 | }; 127 | #endif 128 | -------------------------------------------------------------------------------- /train/LearnGAB.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _LEARNGAB_HPP 2 | #define _LEARNGAB_HPP 3 | #include "common.hpp" 4 | #include "LearnDQT.hpp" 5 | #include 6 | #include "data.hpp" 7 | 8 | /* 9 | * breif The Detector for face classification 10 | */ 11 | class GAB{ 12 | public: 13 | /* 14 | * \breif Init Feature map 15 | * Not only generate feature map and also generate feature-coordinate map 16 | */ 17 | GAB(); 18 | /* 19 | * \breif Train Detector 20 | * Soft Cascade structure 21 | * 22 | * \param pos positive dataset for training 23 | * \param neg negative dataset for training 24 | */ 25 | void LearnGAB(DataSet& pos, DataSet& neg); 26 | /* 27 | * \breif Store a stage 28 | * 29 | * \param feaId vector saved features index trained in this stage 30 | * \param leftChild vector saved tree structure trained in this stage 31 | * \param rightChild vector saved tree structure trained in this stage 32 | * \param cutpoint vector saved double thresholds of a feature 33 | * \param fit vector saved leaves score trained in this stage 34 | * \param threshold threshold for this stage 35 | */ 36 | void SaveIter(vector feaId, vector leftChild, vector rightChild, vector< vector > cutpoint, vector fit, float threshold); 37 | /* 38 | * \bref Save dector to file 39 | */ 40 | void Save(); 41 | /* 42 | * \breif Get the depth of the tree 43 | * 44 | * \param leftChild vector saved tree structure trained in this stage 45 | * \param rightChild vector saved tree structure trained in this stage 46 | * \param node index of node in the tree 47 | */ 48 | int CalcTreeDepth(vector leftChild, vector rightChild, int node = 0); 49 | /* 50 | * \breif Update scores 51 | * score will set to be 0 as the first time 52 | * score will be accumulated in every stages 53 | * it will call function TestSubTree going through to a leaf 54 | * 55 | * \param posFx[] scores to be accumulated 56 | * \param fit vector saved leaves score trained in this stage 57 | * \param cutpoint vector saved double thresholds of a feature 58 | * \param leftChild vector saved tree structure trained in this stage 59 | * \param rightChild vector saved tree structure trained in this stage 60 | * \param x Feature Mat calculated by Pixel Mat 61 | */ 62 | void TestDQT(float posFx[], vector fit, vector< vector > cutpoint, vector leftChild, vector rightChild, cv::Mat x); 63 | /* 64 | * \breif Go through the tree to get a leaf score 65 | * The function has a index indicate which img to go throw the tree, 66 | * be different with matlab code which going through the tree all imgs together. 67 | * 68 | * \param fit ... 69 | * \param cutpoint ... 70 | * \param x Featue Mat 71 | * \param node index of node in the tree 72 | * \param index index of image 73 | * \param leftChild ... 74 | * \param rightChild ... 75 | */ 76 | float TestSubTree(vector fit,vector< vector > cutpoint,cv::Mat x,int node,int index,vector leftChild, vector rightChild); 77 | /* 78 | * \breif Validate the region is a face or not 79 | * Go throw all the stages and accumulate the scores, 80 | * only if the score passed all the threshold, judge it to be a face 81 | * 82 | * param test the region to be test 83 | * param score the score finally it got 84 | * param sIndex the index of winSize 85 | */ 86 | bool NPDClassify(cv::Mat test,float &score,int sIndex); 87 | /* 88 | * \breif Get the coordinates by feature id 89 | * the feature number is calculate by (objSize*objSize)*(objSize*objSize-1)/2 90 | * so if you have a feature id, use this function to get the coordinates 91 | * got the coordinates you can calculate the feature value in image. 92 | * here use two maps which store feature-coordinates 93 | * 94 | * \param feaid Feature Id 95 | * \param x1 coordinate of point A.x 96 | * \param x2 coordinate of point A.y 97 | * \param y1 coordinate of point B.x 98 | * \param y2 coordinate of point B.y 99 | */ 100 | void GetPoints(int feaid, int *x1, int *y1, int *x2, int *y2); 101 | /* 102 | * \breif Get the pixel index by feature id 103 | * same to be Previous, use this function to get two pixel index 104 | * 105 | * \param x pixel index of point A 106 | * \param y pixel index of point B 107 | */ 108 | void GetPoints(int feaid, int *x, int *y); 109 | /* 110 | * \breif Mining Negative Samples 111 | * Use NextImage to get regions and than use NPDClassify to validate it's a face or not 112 | * Using a Mining rate to control travel speed 113 | * Using region_pool for openmp 114 | * 115 | * \param n the negative size final condition 116 | * \param neg negative dataset 117 | */ 118 | void MiningNeg(const int n,DataSet& neg); 119 | /* 120 | * \breif Load a model to detector 121 | * 122 | * \param path file path of model 123 | */ 124 | void LoadModel(string path); 125 | /* 126 | * \breif Draw rect in a image 127 | * 128 | * \param img the image need to be draw 129 | * \param rects the box 130 | */ 131 | cv::Mat Draw(cv::Mat& img, cv::Rect& rects); 132 | public: 133 | /* \breif indicate how many stages the dector have */ 134 | int stages; 135 | /* \breif vectors contain the model */ 136 | vector treeIndex; 137 | vector feaIds, leftChilds, rightChilds; 138 | vector cutpoints; 139 | vector fits; 140 | vector thresholds; 141 | int numBranchNodes; 142 | /* \breif save the points of feature id */ 143 | vector< vector > points1x,points2x,points1y,points2y; 144 | /* \breif vector contain point-feature map */ 145 | vector lpoints; 146 | vector rpoints; 147 | /* \breif A feature map used for speed up calculate feature */ 148 | cv::Mat ppNpdTable; 149 | /* \breif model template size */ 150 | int DetectSize; 151 | }; 152 | #endif 153 | -------------------------------------------------------------------------------- /train/data.cpp: -------------------------------------------------------------------------------- 1 | #include "data.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace cv; 7 | 8 | 9 | DataSet::DataSet(){ 10 | const Options& opt = Options::GetInstance(); 11 | 12 | int i; 13 | for(i=0;i is_flip; 42 | char buff[300]; 43 | vector path; 44 | vector bboxes; 45 | 46 | if(!opt.augment || stages == 0){ 47 | file = fopen(positive.c_str(), "r"); 48 | while (fscanf(file, "%s", buff) > 0) { 49 | path.push_back(string(buff)); 50 | Rect bbox; 51 | fscanf(file, "%d%d%d%d", &bbox.x, &bbox.y, &bbox.width, &bbox.height); 52 | bboxes.push_back(bbox); 53 | is_flip.push_back(0); 54 | } 55 | } 56 | else{ 57 | file = fopen(opt.tmpfile.c_str(), "r"); 58 | while (fscanf(file, "%s", buff) > 0) { 59 | path.push_back(string(buff)); 60 | Rect bbox; 61 | int flip; 62 | fscanf(file, "%d%d%d%d%d", &bbox.x, &bbox.y, &bbox.width, &bbox.height, &flip); 63 | is_flip.push_back(flip); 64 | bboxes.push_back(bbox); 65 | } 66 | } 67 | 68 | fclose(file); 69 | const int n = path.size(); 70 | size = n; 71 | if(!opt.augment || stages != 0){ 72 | imgs.resize(size); 73 | #pragma omp parallel for 74 | for (int i = 0; i < n; i++) { 75 | Mat origin = imread(path[i], CV_LOAD_IMAGE_GRAYSCALE); 76 | if (!origin.data) { 77 | printf("Can not open %s",path[i].c_str()); 78 | } 79 | Mat tmp = origin.clone(); 80 | if(is_flip[i]) 81 | flip(tmp,tmp,1); 82 | Mat face = tmp(bboxes[i]); 83 | Mat img; 84 | cv::resize(face, img, Size(opt.objSize, opt.objSize)); 85 | imgs[i] = img.clone(); 86 | } 87 | } 88 | else 89 | { 90 | FILE* tmpfile = fopen(opt.tmpfile.c_str(),"w"); 91 | size = size*10; 92 | imgs.resize(size); 93 | #pragma omp parallel for 94 | for(int i = 0; i < n; i++) { 95 | Mat origin = imread(path[i], CV_LOAD_IMAGE_GRAYSCALE); 96 | if (!origin.data) { 97 | printf("Can not open %s",path[i].c_str()); 98 | } 99 | int type = 0; 100 | int stmp,xtmp,ytmp; 101 | int s = bboxes[i].width; 102 | 103 | while(type < 5){ 104 | srand(time(0)+type); 105 | do{ 106 | stmp = s*((float)(rand()%20)/100.0+0.9); 107 | xtmp = bboxes[i].x+s*((float)(rand()%10)/100.0-0.05); 108 | ytmp = bboxes[i].y+s*((float)(rand()%10)/100.0-0.05); 109 | }while(xtmp<0 || ytmp<0 || (xtmp+stmp)>origin.cols || (ytmp+stmp)>origin.rows); 110 | Rect rect(xtmp,ytmp,stmp,stmp); 111 | fprintf(tmpfile,"%s %d %d %d %d 0\n",path[i].c_str(),xtmp,ytmp,stmp,stmp); 112 | 113 | Mat face = origin(rect); 114 | Mat img; 115 | cv::resize(face, img, Size(opt.objSize, opt.objSize)); 116 | imgs[i*10+type]=img.clone(); 117 | type ++; 118 | } 119 | 120 | flip(origin,origin,1); 121 | bboxes[i].x = origin.cols - bboxes[i].x - bboxes[i].width; 122 | 123 | while(type < 10){ 124 | srand(time(0)+type); 125 | do{ 126 | stmp = s*((float)(rand()%20)/100.0+0.9); 127 | xtmp = bboxes[i].x+s*((float)(rand()%10)/100.0-0.05); 128 | ytmp = bboxes[i].y+s*((float)(rand()%10)/100.0-0.05); 129 | }while(xtmp<0 || ytmp<0 || (xtmp+stmp)>origin.cols || (ytmp+stmp)>origin.rows); 130 | Rect rect(xtmp,ytmp,stmp,stmp); 131 | fprintf(tmpfile,"%s %d %d %d %d 1\n",path[i].c_str(),xtmp,ytmp,stmp,stmp); 132 | 133 | Mat face = origin(rect); 134 | Mat img; 135 | cv::resize(face, img, Size(opt.objSize, opt.objSize)); 136 | imgs[i*10+type]=img.clone(); 137 | type ++; 138 | } 139 | } 140 | fclose(tmpfile); 141 | } 142 | random_shuffle(imgs.begin(),imgs.end()); 143 | initWeights(); 144 | } 145 | 146 | void DataSet::LoadNegativeDataSet(const string& negative, int pos_num, int stages){ 147 | const Options& opt = Options::GetInstance(); 148 | FILE* file = fopen(negative.c_str(), "r"); 149 | char buff[256]; 150 | list.clear(); 151 | while (fscanf(file, "%s", buff) > 0) { 152 | list.push_back(buff); 153 | } 154 | size = pos_num; 155 | random_shuffle(list.begin(),list.end()); 156 | imgs.reserve(size + opt.numThreads); 157 | initWeights(); 158 | for(int k = 0; k 0 && count < n) { 172 | Mat img = imread(buff,CV_LOAD_IMAGE_GRAYSCALE); 173 | imgs.push_back(img); 174 | count ++; 175 | } 176 | if(count != n) 177 | printf("hd imgs not enough!\n"); 178 | } 179 | else{ 180 | int pool_size = opt.numThreads; 181 | vector region_pool(pool_size); 182 | int st = 0; 183 | int need = n - st; 184 | 185 | while(st(width-win[i])){ 247 | x[i] = 0; 248 | y[i]+=step[i]; 249 | if(y[i]>(height-win[i])){ 250 | y[i] = 0; 251 | win[i]*=factor[i]; 252 | if(win[i]>width || win[i]>height){ 253 | win[i]=opt.objSize; 254 | tranType[i]++; 255 | if(tranType[i]>7){ 256 | tranType[i] = 0; 257 | current_id[i]+=opt.numThreads; 258 | if(current_id[i]>=list.size()){ 259 | current_id[i]=i; 260 | Mat tmg = imread(list[current_id[i]],CV_LOAD_IMAGE_GRAYSCALE); 261 | NegImgs[i] = tmg.clone(); 262 | srand(time(0)+i); 263 | factor[i] = 1.+(float)(rand()%50)/100.0; 264 | step[i] = 12+rand()%12; 265 | } 266 | else{ 267 | Mat tmg = imread(list[current_id[i]],CV_LOAD_IMAGE_GRAYSCALE); 268 | NegImgs[i] = tmg.clone(); 269 | } 270 | } 271 | } 272 | } 273 | } 274 | return region; 275 | } 276 | 277 | void DataSet::ImgClear(){ 278 | imgs.clear(); 279 | vector blank; 280 | imgs.swap(blank); 281 | } 282 | 283 | void DataSet::Remove(vector PassIndex){ 284 | const Options& opt = Options::GetInstance(); 285 | int passNum = PassIndex.size(); 286 | 287 | vector tmpImgs; 288 | float* tmpFx = new float[size+opt.numThreads]; 289 | for(int i = 0;i(i,k) = img.at(x,y); 328 | } 329 | } 330 | return fea; 331 | } 332 | 333 | void DataSet::initWeights(){ 334 | Options& opt = Options::GetInstance(); 335 | W = new float[size]; 336 | for(int i = 0;i 0 || j > 0) fea = double(i) / (double(i) + double(j)); 13 | fea = floor(256 * fea); 14 | if(fea > 255) fea = 255; 15 | 16 | ppNpdTable.at(i,j) = (unsigned char) fea; 17 | } 18 | } 19 | 20 | size_t numPixels = opt.objSize*opt.objSize; 21 | for(int i = 0; i < numPixels; i++) 22 | { 23 | for(int j = i+1; j < numPixels; j ++) 24 | { 25 | lpoints.push_back(i); 26 | rpoints.push_back(j); 27 | } 28 | } 29 | } 30 | 31 | float DQT::Learn(cv::Mat posX,cv::Mat negX, float pPosW[], float pNegW[], vector posIndex,vector negIndex, int minLeaf, vector &feaId, vector &leftChild, vector &rightChild, vector< vector > &cutpoint, vector &fit){ 32 | const Options& opt = Options::GetInstance(); 33 | int treeLevel = opt.treeLevel; 34 | int numThreads = opt.numThreads; 35 | int nTotalPos = posX.cols; 36 | int nTotalNeg = negX.cols; 37 | int nPos = posIndex.size(); 38 | int nNeg = negIndex.size(); 39 | int numPixels = opt.objSize*opt.objSize; 40 | 41 | vector ppPosX(numPixels); 42 | ppPosX[0] = (unsigned char *)posX.data; 43 | for(int i = 1; i < numPixels; i++) ppPosX[i] = ppPosX[i-1] + nTotalPos; 44 | 45 | vector ppNegX(numPixels); 46 | ppNegX[0] = (unsigned char *)negX.data; 47 | for(int i = 1; i < numPixels; i++) ppNegX[i] = ppNegX[i-1] + nTotalNeg; 48 | 49 | int pPosIndex[nPos]; 50 | int pNegIndex[nNeg]; 51 | 52 | for(int i =0;i &posX, vector &negX, float *posW, float *negW, int *posIndex, int *negIndex, int nPos, int nNeg, int treeLevel, int minLeaf, int numThreads, float parentFit, vector &feaId, vector< vector > &cutpoint, vector &leftChild, vector &rightChild, vector &fit) 64 | { 65 | int _feaId; 66 | unsigned char _cutpoint[2]; 67 | float _fit[2]; 68 | 69 | float minCost = LearnQuadStump(posX, negX, posW, negW, posIndex, negIndex, nPos, nNeg, minLeaf, numThreads, parentFit, _feaId, _cutpoint, _fit); 70 | 71 | if(_feaId < 0) return minCost; 72 | 73 | feaId.push_back(_feaId); 74 | cutpoint.push_back(vector(_cutpoint, _cutpoint+2)); 75 | leftChild.push_back(-1); 76 | rightChild.push_back(-2); 77 | 78 | if(treeLevel <= 1) 79 | { 80 | fit.push_back(_fit[0]); 81 | fit.push_back(_fit[1]); 82 | return minCost; 83 | } 84 | 85 | int nPos1 = 0, nNeg1 = 0, nPos2 = 0, nNeg2 = 0; 86 | vector posIndex1(nPos), posIndex2(nPos), negIndex1(nNeg), negIndex2(nNeg); 87 | 88 | for(int j = 0; j < nPos; j++) 89 | { 90 | int x,y; 91 | GetPoints(_feaId,&x,&y); 92 | unsigned char Fea = ppNpdTable.at(posX[size_t(x)][size_t(posIndex[j])],posX[size_t(y)][size_t(posIndex[j])]); 93 | 94 | if(Fea < _cutpoint[0] || Fea > _cutpoint[1]) 95 | { 96 | posIndex1[nPos1++] = posIndex[j]; 97 | } 98 | else 99 | { 100 | posIndex2[nPos2++] = posIndex[j]; 101 | } 102 | } 103 | 104 | for(int j = 0; j < nNeg; j++) 105 | { 106 | int x,y; 107 | GetPoints(_feaId,&x,&y); 108 | unsigned char Fea = ppNpdTable.at(negX[size_t(x)][size_t(negIndex[j])],negX[size_t(y)][size_t(negIndex[j])]); 109 | 110 | if(Fea < _cutpoint[0] || Fea > _cutpoint[1]) 111 | { 112 | negIndex1[nNeg1++] = negIndex[j]; 113 | } 114 | else 115 | { 116 | negIndex2[nNeg2++] = negIndex[j]; 117 | } 118 | } 119 | 120 | vector feaId1, feaId2, leftChild1, leftChild2, rightChild1, rightChild2; 121 | vector< vector > cutpoint1, cutpoint2; 122 | vector fit1, fit2; 123 | 124 | float minCost1 = LearnDQT(posX, negX, posW, negW, &posIndex1[0], &negIndex1[0], nPos1, nNeg1, treeLevel - 1, minLeaf, numThreads, _fit[0], feaId1, cutpoint1, leftChild1, rightChild1, fit1); 125 | 126 | float minCost2 = LearnDQT(posX, negX, posW, negW, &posIndex2[0], &negIndex2[0], nPos2, nNeg2, treeLevel - 1, minLeaf, numThreads, _fit[1], feaId2, cutpoint2, leftChild2, rightChild2, fit2); 127 | 128 | if(feaId1.empty() && feaId2.empty()) 129 | { 130 | fit.push_back(_fit[0]); 131 | fit.push_back(_fit[1]); 132 | return minCost; 133 | } 134 | 135 | if(minCost1 + minCost2 >= minCost) 136 | { 137 | fit.push_back(_fit[0]); 138 | fit.push_back(_fit[1]); 139 | return minCost; 140 | } 141 | 142 | minCost = minCost1 + minCost2; 143 | 144 | if(feaId1.empty()) 145 | { 146 | fit.push_back(_fit[0]); 147 | } 148 | else 149 | { 150 | feaId.insert(feaId.end(), feaId1.begin(), feaId1.end()); 151 | cutpoint.insert(cutpoint.end(), cutpoint1.begin(), cutpoint1.end()); 152 | fit = fit1; 153 | 154 | for(int i = 0; i < leftChild1.size(); i++) 155 | { 156 | if(leftChild1[i] >= 0) leftChild1[i]++; 157 | if(rightChild1[i] >= 0) rightChild1[i]++; 158 | } 159 | 160 | leftChild[0] = 1; 161 | leftChild.insert(leftChild.end(), leftChild1.begin(), leftChild1.end()); 162 | rightChild.insert(rightChild.end(), rightChild1.begin(), rightChild1.end()); 163 | } 164 | 165 | int numBranchNodes = (int) feaId.size(); 166 | int numLeafNodes = (int) fit.size(); 167 | 168 | if(feaId2.empty()) 169 | { 170 | fit.push_back(_fit[1]); 171 | rightChild[0] = -(numLeafNodes + 1); 172 | } 173 | else 174 | { 175 | feaId.insert(feaId.end(), feaId2.begin(), feaId2.end()); 176 | cutpoint.insert(cutpoint.end(), cutpoint2.begin(), cutpoint2.end()); 177 | fit.insert(fit.end(), fit2.begin(), fit2.end()); 178 | 179 | for(int i = 0; i < leftChild2.size(); i++) 180 | { 181 | if(leftChild2[i] >= 0) leftChild2[i] += numBranchNodes; 182 | else leftChild2[i] -= numLeafNodes; 183 | 184 | if(rightChild2[i] >= 0) rightChild2[i] += numBranchNodes; 185 | else rightChild2[i] -= numLeafNodes; 186 | } 187 | 188 | leftChild.insert(leftChild.end(), leftChild2.begin(), leftChild2.end()); 189 | rightChild[0] = numBranchNodes; 190 | rightChild.insert(rightChild.end(), rightChild2.begin(), rightChild2.end()); 191 | } 192 | 193 | return minCost; 194 | } 195 | 196 | 197 | float DQT::LearnQuadStump(vector &posX, vector &negX, float *posW, float *negW, int *posIndex, int *negIndex, int nPos, int nNeg, int minLeaf, int numThreads, float parentFit, int &feaId, unsigned char (&cutpoint)[2], float (&fit)[2]) 198 | { 199 | float w = 0; 200 | for(int i = 0; i < nPos; i++) w += posW[ posIndex[i] ]; 201 | float minCost = w * (parentFit - 1) * (parentFit - 1); 202 | 203 | w = 0; 204 | for(int i = 0; i < nNeg; i++) w += negW[ negIndex[i] ]; 205 | minCost += w * (parentFit + 1) * (parentFit + 1); 206 | 207 | feaId = -1; 208 | if(nPos == 0 || nNeg == 0 || nPos + nNeg < 2 * minLeaf) return minCost; 209 | 210 | int feaDims = (int) posX.size()*(posX.size()-1)/2; 211 | minCost = 1e16f; 212 | 213 | omp_set_num_threads(numThreads); 214 | 215 | // process each dimension 216 | #pragma omp parallel for 217 | for(int i = 0; i < feaDims; i++) 218 | { 219 | int count[256]; 220 | float posWHist[256]; 221 | float negWHist[256]; 222 | 223 | memset(count, 0, 256 * sizeof(int)); 224 | 225 | int x,y; 226 | GetPoints(i,&x,&y); 227 | 228 | WeightHist(posX[x], posX[y], posW, posIndex, nPos, count, posWHist); 229 | WeightHist(negX[x], negX[y], negW, negIndex, nNeg, count, negWHist); 230 | 231 | float posWSum = 0; 232 | float negWSum = 0; 233 | 234 | for(int bin = 0; bin < 256; bin++) 235 | { 236 | posWSum += posWHist[bin]; 237 | negWSum += negWHist[bin]; 238 | } 239 | 240 | int totalCount = nPos + nNeg; 241 | float wSum = posWSum + negWSum; 242 | 243 | float minMSE = 3.4e38f; 244 | int thr0 = -1, thr1; 245 | float fit0, fit1; 246 | 247 | for(int v = 0; v < 256; v++) // lower threshold 248 | { 249 | int rightCount = 0; 250 | float rightPosW = 0; 251 | float rightNegW = 0; 252 | 253 | for(int u = v; u < 256; u++) // upper threshold 254 | { 255 | rightCount += count[u]; 256 | rightPosW += posWHist[u]; 257 | rightNegW += negWHist[u]; 258 | 259 | if(rightCount < minLeaf) continue; 260 | 261 | int leftCount = totalCount - rightCount; 262 | if(leftCount < minLeaf) break; 263 | 264 | float leftPosW = posWSum - rightPosW; 265 | float leftNegW = negWSum - rightNegW; 266 | 267 | float leftFit, rightFit; 268 | 269 | if(leftPosW + leftNegW <= 0) leftFit = 0.0f; 270 | else leftFit = (leftPosW - leftNegW) / (leftPosW + leftNegW); 271 | 272 | if(rightPosW + rightNegW <= 0) rightFit = 0.0f; 273 | else rightFit = (rightPosW - rightNegW) / (rightPosW + rightNegW); 274 | 275 | float leftMSE = leftPosW * (leftFit - 1) * (leftFit - 1) + leftNegW * (leftFit + 1) * (leftFit + 1); 276 | float rightMSE = rightPosW * (rightFit - 1) * (rightFit - 1) + rightNegW * (rightFit + 1) * (rightFit + 1); 277 | 278 | float mse = leftMSE + rightMSE; 279 | 280 | if(mse < minMSE) 281 | { 282 | minMSE = mse; 283 | thr0 = v; 284 | thr1 = u; 285 | fit0 = leftFit; 286 | fit1 = rightFit; 287 | } 288 | } 289 | } 290 | if(thr0 == -1) continue; 291 | 292 | if(minMSE < minCost) 293 | { 294 | #pragma omp critical // modify the record by a single thread 295 | { 296 | minCost = minMSE; 297 | feaId = i; 298 | cutpoint[0] = (unsigned char) thr0; 299 | cutpoint[1] = (unsigned char) thr1; 300 | fit[0] = fit0; 301 | fit[1] = fit1; 302 | } 303 | } 304 | } // end omp parallel for feaDims 305 | return minCost; 306 | } 307 | 308 | 309 | void DQT::WeightHist(unsigned char *X, unsigned char *Y, float *W, int *index, int n, int count[256], float wHist[256]) 310 | { 311 | memset(wHist, 0, 256 * sizeof(float)); 312 | 313 | for(int j = 0; j < n; j++) 314 | { 315 | unsigned char bin = ppNpdTable.at(X[ index[j] ],Y[ index[j] ]); 316 | count[bin]++; 317 | wHist[bin] += W[ index[j] ]; 318 | } 319 | } 320 | 321 | void DQT::GetPoints(int feaid, int *x, int *y){ 322 | const Options& opt = Options::GetInstance(); 323 | int lpoint = lpoints[feaid]; 324 | int rpoint = rpoints[feaid]; 325 | *x = lpoint; 326 | *y = rpoint; 327 | } 328 | -------------------------------------------------------------------------------- /detection/LearnGAB.cpp: -------------------------------------------------------------------------------- 1 | #include "LearnGAB.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define max(a, b) (((a) > (b)) ? (a) : (b)) 10 | #define min(a, b) (((a) < (b)) ? (a) : (b)) 11 | 12 | using namespace cv; 13 | 14 | int pWinSize[]={24,29,35,41,50,60,72,86,103,124,149,178,214,257,308,370,444,532,639,767,920,1104,1325,1590,1908,2290,2747,3297,3956}; 15 | 16 | GAB::GAB(){ 17 | const Options& opt = Options::GetInstance(); 18 | stages = 0; 19 | 20 | ppNpdTable = Mat(256,256,CV_8UC1); 21 | for(int i = 0; i < 256; i++) 22 | { 23 | for(int j = 0; j < 256; j++) 24 | { 25 | double fea = 0.5; 26 | if(i > 0 || j > 0) fea = double(i) / (double(i) + double(j)); 27 | fea = floor(256 * fea); 28 | if(fea > 255) fea = 255; 29 | 30 | ppNpdTable.at(i,j) = (unsigned char) fea; 31 | } 32 | } 33 | 34 | size_t numPixels = opt.objSize*opt.objSize; 35 | for(int i = 0; i < numPixels; i++) 36 | { 37 | for(int j = i+1; j < numPixels; j ++) 38 | { 39 | lpoints.push_back(i); 40 | rpoints.push_back(j); 41 | } 42 | } 43 | 44 | points1.resize(29); 45 | points2.resize(29); 46 | 47 | } 48 | 49 | void GAB::GetPoints(int feaid, int *x1, int *y1, int *x2, int *y2){ 50 | const Options& opt = Options::GetInstance(); 51 | int lpoint = lpoints[feaid]; 52 | int rpoint = rpoints[feaid]; 53 | //use the model trained by yourself 54 | *y1 = lpoint%opt.objSize; 55 | *x1 = lpoint/opt.objSize; 56 | *y2 = rpoint%opt.objSize; 57 | *x2 = rpoint/opt.objSize; 58 | //use the 1226model 59 | /* 60 | *y1 = lpoint/opt.objSize; 61 | *x1 = lpoint%opt.objSize; 62 | *y2 = rpoint/opt.objSize; 63 | *x2 = rpoint%opt.objSize; 64 | */ 65 | } 66 | 67 | void GAB::LoadModel(string path){ 68 | FILE* file; 69 | if((file = fopen(path.c_str(), "rb"))==NULL) 70 | return; 71 | 72 | fread(&DetectSize,sizeof(int),1,file); 73 | fread(&stages,sizeof(int),1,file); 74 | fread(&numBranchNodes,sizeof(int),1,file); 75 | printf("stages num :%d\n",stages); 76 | 77 | int *_tree = new int[stages]; 78 | float *_threshold = new float[stages]; 79 | fread(_tree,sizeof(int),stages,file); 80 | fread(_threshold,sizeof(float),stages,file); 81 | for(int i = 0;i GAB::DetectFace(Mat img,vector& rects,vector& scores){ 131 | const Options& opt = Options::GetInstance(); 132 | 133 | int minFace = 20; 134 | int maxFace = 3000; 135 | 136 | omp_set_dynamic(1); 137 | 138 | int height = img.rows; 139 | int width = img.cols; 140 | int sizePatch = min(width,height); 141 | int thresh = 0; 142 | const unsigned char *O = (unsigned char *) img.data; 143 | unsigned char *I = new unsigned char[width*height]; 144 | int k =0; 145 | for(int i = 0;i= pWinSize[i]) 156 | { 157 | thresh = i; 158 | continue; 159 | } 160 | else 161 | { 162 | break; 163 | } 164 | } 165 | 166 | thresh = thresh + 1; // the total no. of scales that will be searched 167 | 168 | minFace = max(minFace, opt.objSize); 169 | maxFace = min(maxFace, min(height, width)); 170 | 171 | vector picked; 172 | if(min(height, width) < minFace) 173 | { 174 | return picked; 175 | } 176 | for(int k = 0; k < thresh; k++) // process each scale 177 | { 178 | if(pWinSize[k] < minFace) continue; 179 | else if(pWinSize[k] > maxFace) break; 180 | 181 | // determine the step of the sliding subwindow 182 | int winStep = (int) floor(pWinSize[k] * 0.1); 183 | if(pWinSize[k] > 40) winStep = (int) floor(pWinSize[k] * 0.05); 184 | 185 | // calculate the offset values of each pixel in a subwindow 186 | // pre-determined offset of pixels in a subwindow 187 | vector offset(pWinSize[k] * pWinSize[k]); 188 | int pp1 = 0, pp2 = 0, gap = height - pWinSize[k]; 189 | 190 | for(int j=0; j < pWinSize[k]; j++) // column coordinate 191 | { 192 | for(int i = 0; i < pWinSize[k]; i++) // row coordinate 193 | { 194 | offset[pp1++] = pp2++; 195 | } 196 | 197 | pp2 += gap; 198 | } 199 | int colMax = width - pWinSize[k] + 1; 200 | int rowMax = height - pWinSize[k] + 1; 201 | 202 | // process each subwindow 203 | #pragma omp parallel for 204 | for(int c = 0; c < colMax; c += winStep) // slide in column 205 | { 206 | const unsigned char *pPixel = I + c * height; 207 | 208 | for(int r = 0; r < rowMax; r += winStep, pPixel += winStep) // slide in row 209 | { 210 | float _score = 0; 211 | int s; 212 | 213 | 214 | // test each tree classifier 215 | for(s = 0; s < stages; s++) 216 | { 217 | int node = treeIndex[s]; 218 | 219 | // test the current tree classifier 220 | while(node > -1) // branch node 221 | { 222 | unsigned char p1 = pPixel[offset[points1[k][node]]]; 223 | unsigned char p2 = pPixel[offset[points2[k][node]]]; 224 | unsigned char fea = ppNpdTable.at(p1,p2); 225 | 226 | if(fea < cutpoints[2*node] || fea > cutpoints[2*node+1]) node = leftChilds[node]; 227 | else node = rightChilds[node]; 228 | 229 | } 230 | 231 | node = - node - 1; 232 | _score = _score + fits[node]; 233 | 234 | if(_score < thresholds[s]){ 235 | break; // negative samples 236 | } 237 | } 238 | 239 | if(s == stages) // a face detected 240 | { 241 | Rect roi(c, r, pWinSize[k], pWinSize[k]); 242 | #pragma omp critical // modify the record by a single thread 243 | { 244 | rects.push_back(roi); 245 | scores.push_back(_score); 246 | } 247 | } 248 | } 249 | } 250 | } 251 | vector Srect; 252 | picked = Nms(rects,scores,Srect,0.5,img); 253 | 254 | int imgWidth = img.cols; 255 | int imgHeight = img.rows; 256 | 257 | 258 | //you should set the parameter by yourself 259 | for(int i = 0;i GAB::Nms(vector& rects, vector& scores, vector& Srect, float overlap, Mat Img) { 279 | int numCandidates = rects.size(); 280 | Mat_ predicate = Mat_::eye(numCandidates, numCandidates); 281 | for(int i = 0;i=overlap){ 288 | predicate(i,j) = 1; 289 | predicate(j,i) = 1; 290 | } 291 | } 292 | } 293 | 294 | vector label; 295 | 296 | int numLabels = Partation(predicate,label); 297 | 298 | vector Rects; 299 | Srect.resize(numLabels); 300 | vector neighbors; 301 | neighbors.resize(numLabels); 302 | vector Score; 303 | Score.resize(numLabels); 304 | 305 | for(int i = 0;i index; 307 | for(int j = 0;j weight; 312 | weight = Logistic(scores,index); 313 | float sumScore=0; 314 | for(int j=0;j::zeros(numLabels, numLabels); 347 | 348 | for(int i = 0;i=overlap || (float)s/(float)(Rects[j].width*Rects[j].height)>=overlap) 355 | { 356 | predicate(i,j) = 1; 357 | predicate(j,i) = 1; 358 | } 359 | } 360 | } 361 | 362 | vector flag; 363 | flag.resize(numLabels); 364 | for(int i = 0;i index; 369 | for(int j = 0;js) 379 | s = Score[index[j]]; 380 | } 381 | if(s>Score[i]) 382 | flag[i]=0; 383 | } 384 | 385 | vector picked; 386 | for(int i = 0;iheight) 404 | Rects[idx].height = height-Rects[idx].y; 405 | 406 | if(Rects[idx].x+Rects[idx].width>width) 407 | Rects[idx].width= width-Rects[idx].x; 408 | } 409 | 410 | rects = Rects; 411 | scores = Score; 412 | return picked; 413 | } 414 | 415 | vector GAB::Logistic(vector scores ,vector index){ 416 | vector Y; 417 | for(int i = 0;i& predicate,vector& label){ 427 | int N = predicate.cols; 428 | vector parent; 429 | vector rank; 430 | for(int i=0;i rank[root_i]) 446 | parent[root_i] = root_j; 447 | else{ 448 | parent[root_j] = root_i; 449 | rank[root_i] = rank[root_i] + 1; 450 | } 451 | } 452 | } 453 | } 454 | 455 | int nGroups = 0; 456 | label.resize(N); 457 | for(int i=0;i& parent,int x){ 476 | int root = parent[x]; 477 | if(root != x) 478 | root = Find(parent,root); 479 | return root; 480 | } 481 | 482 | 483 | Mat GAB::Draw(Mat& img, Rect& rects){ 484 | Mat img_ = img.clone(); 485 | rectangle(img_,rects,Scalar(0, 0, 255), 2); 486 | return img_; 487 | } 488 | -------------------------------------------------------------------------------- /train/LearnGAB.cpp: -------------------------------------------------------------------------------- 1 | #include "LearnGAB.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define max(a, b) (((a) > (b)) ? (a) : (b)) 9 | #define min(a, b) (((a) < (b)) ? (a) : (b)) 10 | 11 | using namespace cv; 12 | 13 | int pWinSize[]={24,29,35,41,50,60,72,86,103,124,149,178,214,257,308,370,444,532,639,767,920,1104,1325,1590,1908,2290,2747,3297,3956}; 14 | 15 | GAB::GAB(){ 16 | const Options& opt = Options::GetInstance(); 17 | stages = 0; 18 | 19 | ppNpdTable = Mat(256,256,CV_8UC1); 20 | for(int i = 0; i < 256; i++) 21 | { 22 | for(int j = 0; j < 256; j++) 23 | { 24 | double fea = 0.5; 25 | if(i > 0 || j > 0) fea = double(i) / (double(i) + double(j)); 26 | fea = floor(256 * fea); 27 | if(fea > 255) fea = 255; 28 | 29 | ppNpdTable.at(i,j) = (unsigned char) fea; 30 | } 31 | } 32 | 33 | size_t numPixels = opt.objSize*opt.objSize; 34 | for(int i = 0; i < numPixels; i++) 35 | { 36 | for(int j = i+1; j < numPixels; j ++) 37 | { 38 | lpoints.push_back(i); 39 | rpoints.push_back(j); 40 | } 41 | } 42 | 43 | 44 | points1x.resize(29); 45 | points1y.resize(29); 46 | points2x.resize(29); 47 | points2y.resize(29); 48 | 49 | numBranchNodes = 0; 50 | } 51 | 52 | void GAB::LearnGAB(DataSet& pos, DataSet& neg){ 53 | const Options& opt = Options::GetInstance(); 54 | timeval start, end; 55 | timeval Tstart, Tend; 56 | float time = 0; 57 | int nPos = pos.size; 58 | int nNeg = neg.size; 59 | 60 | float _FAR=1.0; 61 | int nFea=0; 62 | float aveEval=0; 63 | 64 | float *wP = new float[nPos]; 65 | float *wN = new float[nNeg]; 66 | 67 | if(stages!=0){ 68 | 69 | int fail = 0; 70 | #pragma omp parallel for 71 | for (int i = 0; i < nPos; i++) { 72 | float score = 0; 73 | if(NPDClassify(pos.imgs[i],score,0)){ 74 | pos.Fx[i]=score; 75 | } 76 | else{ 77 | fail ++; 78 | } 79 | } 80 | if(fail!=0){ 81 | printf("you should't change pos data! %d \n",fail); 82 | return; 83 | } 84 | 85 | 86 | MiningNeg(nPos*opt.negRatio,neg); 87 | if(neg.imgs.size() posIndex; 109 | vector negIndex; 110 | for(int i=0; i=opt.trimFrac){ 123 | k = i; 124 | break; 125 | } 126 | } 127 | vector< int >::iterator iter; 128 | for(iter = posIndex.begin();iter!=posIndex.end();){ 129 | if(pos.W[*iter]=opt.trimFrac){ 141 | k = i; 142 | break; 143 | } 144 | } 145 | for(iter = negIndex.begin();iter!=negIndex.end();){ 146 | if(neg.W[*iter] feaId, leftChild, rightChild; 158 | vector< vector > cutpoint; 159 | vector fit; 160 | 161 | printf("Iter %d: nPos=%d, nNeg=%d, ", t, nPosSam, nNegSam); 162 | DQT dqt; 163 | gettimeofday(&Tstart,NULL); 164 | float mincost = dqt.Learn(faceFea,nonfaceFea,pos.W,neg.W,posIndex,negIndex,minLeaf_t,feaId,leftChild,rightChild,cutpoint,fit); 165 | gettimeofday(&Tend,NULL); 166 | float DQTtime = (Tend.tv_sec - Tstart.tv_sec); 167 | printf("DQT time:%.3fs\n",DQTtime); 168 | 169 | if (feaId.empty()){ 170 | printf("\n\nNo available features to satisfy the split. The AdaBoost learning terminates.\n"); 171 | break; 172 | } 173 | 174 | Mat posX(feaId.size(),faceFea.cols,CV_8UC1); 175 | for(int i = 0;i(faceFea.at(x,j),faceFea.at(y,j)); 180 | posX.at(i,j) = Fea; 181 | } 182 | Mat negX(feaId.size(),nonfaceFea.cols,CV_8UC1); 183 | for(int i = 0;i(nonfaceFea.at(x,j),nonfaceFea.at(y,j)); 188 | negX.at(i,j) = Fea; 189 | } 190 | 191 | TestDQT(pos.Fx,fit,cutpoint,leftChild,rightChild,posX); 192 | TestDQT(neg.Fx,fit,cutpoint,leftChild,rightChild,negX); 193 | 194 | 195 | vector negPassIndex; 196 | for(int i=0; i feaId, vector leftChild, vector rightChild, vector< vector > cutpoint, vector fit, float threshold){ 265 | const Options& opt = Options::GetInstance(); 266 | 267 | int root = numBranchNodes; 268 | treeIndex.push_back(root); 269 | numBranchNodes += feaId.size(); 270 | 271 | for(int i = 0;i leftChild, vector rightChild, int node){ 352 | int depth = 0; 353 | int ld,rd; 354 | if ((node+1)>leftChild.size()) 355 | return depth; 356 | if (leftChild[node]<0) 357 | ld = 0; 358 | else 359 | ld = CalcTreeDepth(leftChild,rightChild,leftChild[node]); 360 | 361 | if (rightChild[node] < 0) 362 | rd = 0; 363 | else 364 | rd = CalcTreeDepth(leftChild, rightChild, rightChild[node]); 365 | 366 | depth = max(ld,rd) + 1; 367 | return depth; 368 | } 369 | 370 | void GAB::TestDQT(float posFx[], vector fit, vector< vector > cutpoint, vector leftChild, vector rightChild, cv::Mat x){ 371 | int n = x.cols; 372 | float *score = new float[n]; 373 | for (int i = 0;i fit, vector< vector > cutpoint, cv::Mat x, int node, int index, vector leftChild, vector rightChild){ 387 | int n = x.cols; 388 | float score = 0; 389 | 390 | if (node<0){ 391 | score=fit[-node-1]; 392 | } 393 | 394 | else{ 395 | bool isLeft; 396 | if(x.at(node,index)(node,index)>cutpoint[node][1]) 397 | isLeft = 1; 398 | else 399 | isLeft = 0; 400 | 401 | if(isLeft) 402 | score = TestSubTree(fit,cutpoint,x,leftChild[node],index,leftChild,rightChild); 403 | else 404 | score = TestSubTree(fit,cutpoint,x,rightChild[node],index,leftChild,rightChild); 405 | } 406 | return score; 407 | } 408 | 409 | bool GAB::NPDClassify(Mat test,float &score, int sIndex){ 410 | const Options& opt = Options::GetInstance(); 411 | float Fx = 0; 412 | 413 | for(int i = 0 ;i -1){ 416 | unsigned char p1 = test.at(points1x[sIndex][node],points1y[sIndex][node]); 417 | unsigned char p2 = test.at(points2x[sIndex][node],points2y[sIndex][node]); 418 | unsigned char fea = ppNpdTable.at(p1,p2); 419 | 420 | if(fea < cutpoints[node*2] || fea > cutpoints[node*2+1]) 421 | node = leftChilds[node]; 422 | else 423 | node = rightChilds[node]; 424 | } 425 | 426 | node = -node -1; 427 | Fx = Fx + fits[node]; 428 | 429 | if(Fx < thresholds[i]){ 430 | return 0; 431 | } 432 | } 433 | score = Fx; 434 | return 1; 435 | } 436 | 437 | void GAB::GetPoints(int feaid, int *x1, int *y1, int *x2, int *y2){ 438 | const Options& opt = Options::GetInstance(); 439 | int lpoint = lpoints[feaid]; 440 | int rpoint = rpoints[feaid]; 441 | *y1 = lpoint%opt.objSize; 442 | *x1 = lpoint/opt.objSize; 443 | *y2 = rpoint%opt.objSize; 444 | *x2 = rpoint/opt.objSize; 445 | } 446 | 447 | void GAB::GetPoints(int feaid, int *x, int *y){ 448 | const Options& opt = Options::GetInstance(); 449 | int lpoint = lpoints[feaid]; 450 | int rpoint = rpoints[feaid]; 451 | *x = lpoint; 452 | *y = rpoint; 453 | } 454 | 455 | void GAB::MiningNeg(int n,DataSet& neg){ 456 | const Options& opt = Options::GetInstance(); 457 | int pool_size = opt.numThreads; 458 | vector region_pool(pool_size); 459 | int st = neg.imgs.size(); 460 | int all = 0; 461 | int need = n - st; 462 | double rate; 463 | 464 | while(st