Repository for OpenCV's extra modules
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

309 lines
12 KiB

// 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<training_sample>* samples_,vector<Point2f>* 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<training_sample>* samples;
vector<Point2f>* sum;
};
class modifySamples : public ParallelLoopBody
{
public:
modifySamples(vector<training_sample>* samples_,vector<Point2f>* 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<training_sample>* samples;
vector<Point2f>* temp;
};
class splitSamples : public ParallelLoopBody
{
public:
splitSamples(vector<training_sample>* samples_,vector< vector<Point2f> >* leftsumresiduals_,vector<unsigned long>* left_count_,unsigned long* num_test_splits_,vector<splitr>* 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<training_sample>* samples;
vector< vector<Point2f> >* leftsumresiduals;
vector<unsigned long>* left_count;
unsigned long* num_test_splits;
vector<splitr>* feats;
};
splitr FacemarkKazemiImpl::getTestSplits(vector<Point2f> 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<Point2f> pixel_coordinates, vector<training_sample>& samples,unsigned long start ,
unsigned long end,splitr& split,vector< vector<Point2f> >& 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<Point2f> > leftsumresiduals;
leftsumresiduals.resize(params.num_test_splits);
vector<splitr> 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<unsigned long> left_count;
left_count.resize(params.num_test_splits);
parallel_for_(Range(start,end),splitSamples(&samples,&leftsumresiduals,&left_count,&params.num_test_splits,&feats));
//Selecting the best split
double best_score =-1;
unsigned long best_feat = 0;
double score = -1;
vector<Point2f> right_sum;
right_sum.resize(sum[node_no].size());
vector<Point2f> left_sum;
left_sum.resize(sum[node_no].size());
unsigned long right_cnt;
for(unsigned long i=0;i<leftsumresiduals.size();i++){
right_cnt = (end-start+1)-left_count[i];
for(unsigned long k=0;k<leftsumresiduals[i].size();k++){
if (right_cnt!=0){
right_sum[k].x=(sum[node_no][k].x-leftsumresiduals[i][k].x)/right_cnt;
right_sum[k].y=(sum[node_no][k].y-leftsumresiduals[i][k].y)/right_cnt;
}
else
right_sum[k]=Point2f(0,0);
if(left_count[i]!=0){
left_sum[k].x=leftsumresiduals[i][k].x/left_count[i];
left_sum[k].y=leftsumresiduals[i][k].y/left_count[i];
}
else
left_sum[k]=Point2f(0,0);
}
Point2f pt1(0,0);
Point2f pt2(0,0);
for(unsigned long k=0;k<left_sum.size();k++){
pt1.x = pt1.x + (float)(left_sum[k].x*left_sum[k].x);
pt2.x = pt2.x + (float)(right_sum[k].x*right_sum[k].x);
pt1.y = pt1.y + (float)(left_sum[k].y*left_sum[k].y);
pt2.y = pt2.y + (float)(right_sum[k].y*right_sum[k].y);
}
score = (double)sqrt(pt1.x+pt1.y)*(double)left_count[i] + (double)sqrt(pt2.x+pt2.y)*(double)right_cnt;
if(score > 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<sum[node_no].size();k++){
sum[2*node_no+2][k].x = sum[node_no][k].x-sum[2*node_no+1][k].x;
sum[2*node_no+2][k].y = sum[node_no][k].y-sum[2*node_no+1][k].y;
}
split = feats[best_feat];
return true;
}
void FacemarkKazemiImpl::createSplitNode(regtree& tree, splitr split,long node_no){
tree_node node;
node.split = split;
node.leaf.clear();
tree.nodes[node_no]=node;
}
void FacemarkKazemiImpl::createLeafNode(regtree& tree,long node_no,vector<Point2f> 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<node_info>& curr,vector<Point2f> pixel_coordinates, vector<training_sample>& samples,
splitr &split , vector< vector<Point2f> >& 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<<mid<<endl;
if(mid==start||mid==end+1)
return false;
node_info _left,_right;
_left.index1 = start;
_left.index2 = mid-1;
_left.depth = _depth +1;
_left.node_no = 2*node_no+1;
_right.index1 = mid;
_right.index2 = end;
_right.depth = _depth +1;
_right.node_no = 2*node_no+2;
curr.push(_left);
curr.push(_right);
return true;
}
bool FacemarkKazemiImpl :: buildRegtree(regtree& tree,vector<training_sample>& samples,vector<Point2f> 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<node_info> curr;
node_info parent;
vector< vector<Point2f> > 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<long,long> 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<Point2f> 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.size();k++){
temp[k].x=(temp[k].x/count)*params.learning_rate;
temp[k].y=(temp[k].y/count)*params.learning_rate;
}
// Modify current shape according to the weak learners.
parallel_for_(Range(range.first,range.second), modifySamples(&(samples),&(temp)));
createLeafNode(tree,node_no,temp);
}
}
else
{
unsigned long count = range.second-range.first +1;
vector<Point2f> 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.size();k++){
temp[k].x=(temp[k].x/count)*params.learning_rate;
temp[k].y=(temp[k].y/count)*params.learning_rate;
}
// Modify current shape according to the weak learners.
parallel_for_(Range(range.first,range.second), modifySamples(&(samples),&(temp)));
createLeafNode(tree,node_no,temp);
curr.pop();
}
}
return true;
}
unsigned long FacemarkKazemiImpl::divideSamples (splitr split,vector<training_sample>& 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