//=============================================================================
vector<Point2f> 
patch_models::
apply_simil(const Mat &S,
        const vector<Point2f> &points)
{
  int n = points.size();
  vector<Point2f> p(n);
  for(int i = 0; i < n; i++){
    p[i].x = S.fl(0,0)*points[i].x + S.fl(0,1)*points[i].y + S.fl(0,2);
    p[i].y = S.fl(1,0)*points[i].x + S.fl(1,1)*points[i].y + S.fl(1,2);
  }return p;
}
//==============================================================================
bool
face_detector::
enough_bounded_points(const Mat &pts,
              const Rect R,
              const float frac)
{
  int n = pts.rows/2,m = 0;
  for(int i = 0; i < n; i++){
    if((pts.fl(2*i  ) >= R.x) && (pts.fl(2*i  ) <= R.x + R.width) &&
       (pts.fl(2*i+1) >= R.y) && (pts.fl(2*i+1) <= R.y + R.height))m++;
  }
  if(float(m)/n >= frac)return true; else return false;
}
//==============================================================================
Mat
shape_model::
center_shape(const Mat &pts)
{
    int n = pts.rows/2; float mx = 0,my = 0;
    for(int i = 0; i < n; i++){
        mx += pts.fl(2*i); my += pts.fl(2*i+1);
    }
    Mat p(2*n,1,CV_32F); mx /= n; my /= n;
    for(int i = 0; i < n; i++){
        p.fl(2*i) = pts.fl(2*i) - mx; p.fl(2*i+1) = pts.fl(2*i+1) - my;
    }return p;
}
//==============================================================================
void
shape_model::
train(const vector<vector<Point2f> > &points,
      const vector<Vec2i> &con,
      const float frac,
      const int kmax)
{
    //vectorize points
    // 把N张图片中的n个点的坐标排成一个N*2n的矩阵
    Mat X = this->pts2mat(points);
    int N = X.cols,n = X.rows/2;
    
    //align shapes
    // 把这些shape先用procustes方法对齐
    Y = this->procrustes(X);
    
    
    //compute rigid transformation
    Mat R = this->calc_rigid_basis(Y);
    
    //compute non-rigid transformation
    Mat P = R.t()*Y; Mat dY = Y - R*P; SVD svd(dY*dY.t());
    int m = min(min(kmax,N-1),n-1);
    float vsum = 0; for(int i = 0; i < m; i++)vsum += svd.w.fl(i);
    float v = 0; int k = 0;
    for(k = 0; k < m; k++){v += svd.w.fl(k); if(v/vsum >= frac){k++; break;}}
    if(k > m)k = m;
    Mat D = svd.u(Rect(0,0,k,2*n));
    
    //combine bases
    V.create(2*n,4+k,CV_32F);
    Mat Vr = V(Rect(0,0,4,2*n)); R.copyTo(Vr);
    Mat Vd = V(Rect(4,0,k,2*n)); D.copyTo(Vd);
    
    //compute variance (normalized wrt scale)
    Mat Q = V.t()*X;
    for(int i = 0; i < N; i++){
        float v = Q.fl(0,i); Mat q = Q.col(i); q /= v;
    }
    e.create(4+k,1,CV_32F);
    pow(Q,2,Q);
    for(int i = 0; i < 4+k; i++){
        if(i < 4)e.fl(i) = -1;
        else e.fl(i) = Q.row(i).dot(Mat::ones(1,N,CV_32F))/(N-1);
    }
    //store connectivity
    if(con.size() > 0){ //default connectivity
        int m = con.size();
        C.create(m,2,CV_32F);
        for(int i = 0; i < m; i++){
            C.at<int>(i,0) = con[i][0]; C.at<int>(i,1) = con[i][1];
        }
    }else{              //user-specified connectivity
        C.create(n,2,CV_32S);
        for(int i = 0; i < n-1; i++){
            C.at<int>(i,0) = i; C.at<int>(i,1) = i+1;
        }
        C.at<int>(n-1,0) = n-1; C.at<int>(n-1,1) = 0;
    }
}
//==============================================================================
//==============================================================================
//==============================================================================
//==============================================================================
//==============================================================================
//==============================================================================
void 
patch_models::
train(ft_data &data,
      const vector<Point2f> &ref,
      const Size psize,
      const Size ssize,
      const bool mirror,
      const float var,
      const float lambda,
      const float mu_init,
      const int nsamples,
      const bool visi)
{
  //set reference shape
  int n = ref.size(); reference = Mat(ref).reshape(1,2*n);
  Size wsize = psize + ssize;

  //train each patch model in turn
  patches.resize(n);
  for(int i = 0; i < n; i++){
    if(visi)cout << "training patch " << i << "..." << endl;
    vector<Mat> images(0);
    for(int j = 0; j < data.n_images(); j++){
      Mat im = data.get_image(j,0);
//        imshow("im",im);
      vector<Point2f> p = data.get_points(j,false);
      Mat pt = Mat(p).reshape(1,2*n);
      Mat S = this->calc_simil(pt),A(2,3,CV_32F); 
      A.fl(0,0) = S.fl(0,0); A.fl(0,1) = S.fl(0,1);
      A.fl(1,0) = S.fl(1,0); A.fl(1,1) = S.fl(1,1);
      A.fl(0,2) = pt.fl(2*i  ) - 
    (A.fl(0,0) * (wsize.width-1)/2 + A.fl(0,1)*(wsize.height-1)/2);
      A.fl(1,2) = pt.fl(2*i+1) - 
    (A.fl(1,0) * (wsize.width-1)/2 + A.fl(1,1)*(wsize.height-1)/2);
      Mat I; warpAffine(im,I,A,wsize,INTER_LINEAR+WARP_INVERSE_MAP);
      images.push_back(I);
      if(mirror){
    im = data.get_image(j,1); 
    p = data.get_points(j,true);
    pt = Mat(p).reshape(1,2*n);
    S = this->calc_simil(pt);
    A.fl(0,0) = S.fl(0,0); A.fl(0,1) = S.fl(0,1);
    A.fl(1,0) = S.fl(1,0); A.fl(1,1) = S.fl(1,1);
    A.fl(0,2) = pt.fl(2*i  ) - 
      (A.fl(0,0) * (wsize.width-1)/2 + A.fl(0,1)*(wsize.height-1)/2);
    A.fl(1,2) = pt.fl(2*i+1) - 
      (A.fl(1,0) * (wsize.width-1)/2 + A.fl(1,1)*(wsize.height-1)/2);
    warpAffine(im,I,A,wsize,INTER_LINEAR+WARP_INVERSE_MAP);
    images.push_back(I);
      }
    }
    patches[i].train(images,psize,var,lambda,mu_init,nsamples,visi);
  }
}
//==============================================================================
float 
face_detector::
calc_scale(const Mat &pts)
{
  Point2f c = this->center_of_mass(pts); int n = pts.rows/2;
  Mat p(2*n,1,CV_32F);
  for(int i = 0; i < n; i++){
    p.fl(2*i  ) = pts.fl(2*i  ) - c.x;
    p.fl(2*i+1) = pts.fl(2*i+1) - c.y;
  }return reference.dot(p)/reference.dot(reference);
}
//=============================================================================
Mat
shape_model::
rot_scale_align(const Mat &src,
                const Mat &dst)
{
    //construct linear system
    int n = src.rows/2; float a=0,b=0,d=0;
    for(int i = 0; i < n; i++){
        d += src.fl(2*i) * src.fl(2*i  ) + src.fl(2*i+1) * src.fl(2*i+1);
        a += src.fl(2*i) * dst.fl(2*i  ) + src.fl(2*i+1) * dst.fl(2*i+1);
        b += src.fl(2*i) * dst.fl(2*i+1) - src.fl(2*i+1) * dst.fl(2*i  );
    }
    a /= d; b /= d;//solved linear system
    return (Mat_<float>(2,2) << a,-b,b,a);
}
//=============================================================================
Mat 
patch_models::
inv_simil(const Mat &S)
{
  Mat Si(2,3,CV_32F);
  float d = S.fl(0,0)*S.fl(1,1) - S.fl(1,0)*S.fl(0,1);
  Si.fl(0,0) = S.fl(1,1)/d; Si.fl(0,1) = -S.fl(0,1)/d;
  Si.fl(1,1) = S.fl(0,0)/d; Si.fl(1,0) = -S.fl(1,0)/d;
  Mat Ri = Si(Rect(0,0,2,2));
  Mat t = -Ri*S.col(2),St = Si.col(2); t.copyTo(St); return Si; 
}
//==============================================================================
void
shape_model::
calc_params(const vector<Point2f> &pts,const Mat weight,const float c_factor)
{
    int n = pts.size(); assert(V.rows == 2*n);
    Mat s = Mat(pts).reshape(1,2*n); //point set to vector format
    if(weight.empty())p = V.t()*s;   //simple projection
    else{                            //scaled projection
        if(weight.rows != n){cout << "Invalid weighting matrix" << endl; abort();}
        int K = V.cols; Mat H = Mat::zeros(K,K,CV_32F),g = Mat::zeros(K,1,CV_32F);
        for(int i = 0; i < n; i++){
            Mat v = V(Rect(0,2*i,K,2)); float w = weight.fl(i);
            H += w*v.t()*v; g += w*v.t()*Mat(pts[i]);
        }
        solve(H,g,p,DECOMP_SVD);
    }this->clamp(c_factor);          //clamp resulting parameters
}