// This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. #include "precomp.hpp" #include "face_alignmentimpl.hpp" using namespace std; namespace cv{ namespace face{ //Threading helper classes class doSum : public ParallelLoopBody { public: doSum(vector* samples_,vector* sum_) : samples(samples_), sum(sum_) { } virtual void operator()( const Range& range) const CV_OVERRIDE { for (int j = range.start; j < range.end; ++j){ for(unsigned long k=0;k<(*samples)[j].shapeResiduals.size();k++){ (*sum)[k]=(*sum)[k]+(*samples)[j].shapeResiduals[k]; } } } private: vector* samples; vector* sum; }; class modifySamples : public ParallelLoopBody { public: modifySamples(vector* samples_,vector* temp_) : samples(samples_), temp(temp_) { } virtual void operator()( const Range& range) const CV_OVERRIDE { for (int j = range.start; j < range.end; ++j){ for(unsigned long k=0;k<(*samples)[j].shapeResiduals.size();k++){ (*samples)[j].shapeResiduals[k]=(*samples)[j].shapeResiduals[k]-(*temp)[k]; (*samples)[j].current_shape[k]=(*samples)[j].actual_shape[k]-(*samples)[j].shapeResiduals[k]; } } } private: vector* samples; vector* temp; }; class splitSamples : public ParallelLoopBody { public: splitSamples(vector* samples_,vector< vector >* leftsumresiduals_,vector* left_count_,unsigned long* num_test_splits_,vector* feats_) : samples(samples_), leftsumresiduals(leftsumresiduals_), left_count(left_count_), num_test_splits(num_test_splits_), feats(feats_) { } virtual void operator()( const Range& range) const CV_OVERRIDE { for (int i = range.start; i < range.end; ++i){ for(unsigned long j=0;j<*(num_test_splits);j++){ (*left_count)[j]++; if ((float)(*samples)[i].pixel_intensities[(unsigned long)(*feats)[j].index1] - (float)(*samples)[i].pixel_intensities[(unsigned long)(*feats)[j].index2] > (*feats)[j].thresh){ for(unsigned long k=0;k<(*samples)[i].shapeResiduals.size();k++){ (*leftsumresiduals)[j][k]=(*leftsumresiduals)[j][k]+(*samples)[i].shapeResiduals[k]; } } } } } private: vector* samples; vector< vector >* leftsumresiduals; vector* left_count; unsigned long* num_test_splits; vector* feats; }; splitr FacemarkKazemiImpl::getTestSplits(vector pixel_coordinates,int seed) { splitr feat; //generates splits whose probability is above a particular threshold. //P(u,v)=e^(-distance/lambda) as described in the research paper //cited above. This helps to select closer pixels hence make efficient //splits. double probability; double check; RNG rng(seed); do { //select random pixel coordinate feat.index1 = rng.uniform(0,params.num_test_coordinates); //select another random coordinate feat.index2 = rng.uniform(0,params.num_test_coordinates); Point2f pt = pixel_coordinates[(unsigned long)feat.index1]-pixel_coordinates[(unsigned long)feat.index2]; double distance = sqrt((pt.x*pt.x)+(pt.y*pt.y)); //calculate the probability probability = exp(-distance/params.lambda); check = rng.uniform(double(0),double(1)); } while(check>probability||feat.index1==feat.index2); feat.thresh =(float)(((rng.uniform(double(0),double(1)))*256 - 128)/2.0); return feat; } bool FacemarkKazemiImpl:: getBestSplit(vector pixel_coordinates, vector& samples,unsigned long start , unsigned long end,splitr& split,vector< vector >& sum,long node_no) { if(samples[0].shapeResiduals.size()!=samples[0].current_shape.size()){ String error_message = "Error while generating split.Residuals are not complete.Aborting...."; CV_Error(Error::StsBadArg, error_message); } //This vector stores the matrices where each matrix represents //sum of the residuals of shapes of samples which go to the left //child after split vector< vector > leftsumresiduals; leftsumresiduals.resize(params.num_test_splits); vector feats; //generate random splits and selects the best split amongst them. for (unsigned long i = 0; i < params.num_test_splits; ++i){ feats.push_back(getTestSplits(pixel_coordinates,i+(int)time(0))); leftsumresiduals[i].resize(samples[0].shapeResiduals.size()); } vector left_count; left_count.resize(params.num_test_splits); parallel_for_(Range(start,end),splitSamples(&samples,&leftsumresiduals,&left_count,¶ms.num_test_splits,&feats)); //Selecting the best split double best_score =-1; unsigned long best_feat = 0; double score = -1; vector right_sum; right_sum.resize(sum[node_no].size()); vector left_sum; left_sum.resize(sum[node_no].size()); unsigned long right_cnt; for(unsigned long i=0;i best_score){ best_score = score; best_feat = i; } } sum[2*node_no+1] = leftsumresiduals[best_feat]; sum[2*node_no+2].resize(sum[node_no].size()); for(unsigned long k=0;k assign){ tree_node node; node.split.index1 = (uint64_t)(-1); node.split.index2 = (uint64_t)(-1); node.leaf = assign; tree.nodes[node_no] = node; } bool FacemarkKazemiImpl :: generateSplit(queue& curr,vector pixel_coordinates, vector& samples, splitr &split , vector< vector >& sum){ long start = curr.front().index1; long end = curr.front().index2; long _depth = curr.front().depth; long node_no =curr.front().node_no; curr.pop(); if(start == end) return false; getBestSplit(pixel_coordinates,samples,start,end,split,sum,node_no); long mid = divideSamples(split, samples, start, end); //cout<& samples,vector pixel_coordinates){ if(samples.size()==0){ String error_message = "Error while building regression tree.Empty samples. Aborting...."; CV_Error(Error::StsBadArg, error_message); } if(pixel_coordinates.size()==0){ String error_message = "Error while building regression tree.No pixel coordinates. Aborting...."; CV_Error(Error::StsBadArg, error_message); } queue curr; node_info parent; vector< vector > sum; const long numNodes =(long)pow(2,params.tree_depth); const long numSplitNodes = numNodes/2 - 1; sum.resize(numNodes+1); sum[0].resize(samples[0].shapeResiduals.size()); parallel_for_(cv::Range(0,(int)samples.size()), doSum(&(samples),&(sum[0]))); parent.index1=0; parent.index2=(long)samples.size()-1; parent.node_no=0; parent.depth=0; curr.push(parent); tree.nodes.resize(numNodes+1); //Total number of split nodes while(!curr.empty()){ pair range= make_pair(curr.front().index1,curr.front().index2); long node_no = curr.front().node_no; splitr split = {0, 0, 0}; //generate a split if(node_no<=numSplitNodes){ if(generateSplit(curr,pixel_coordinates,samples,split,sum)){ createSplitNode(tree,split,node_no); } //create leaf else{ long count = range.second-range.first +1; vector temp; temp.resize(samples[range.first].shapeResiduals.size()); parallel_for_(Range(range.first, range.second), doSum(&(samples),&(temp))); for(unsigned long k=0;k temp; temp.resize(samples[range.first].shapeResiduals.size()); parallel_for_(Range(range.first, range.second), doSum(&(samples),&(temp))); for(unsigned long k=0;k& samples,unsigned long start,unsigned long end) { if(samples.size()==0){ String error_message = "Error while dividing samples. Sample array empty. Aborting...."; CV_Error(Error::StsBadArg, error_message); } unsigned long i = start; training_sample temp; //partition samples according to the split for (unsigned long j = start; j < end; ++j) { if ((float)samples[j].pixel_intensities[(unsigned long)split.index1] - (float)samples[j].pixel_intensities[(unsigned long)split.index2] > split.thresh) { temp=samples[i]; samples[i]=samples[j]; samples[j]=temp; ++i; } } return i; } }//cv }//face