www.pudn.com > ReadingPeopleTracker-1.28.rar > BoundaryPoints.cc


/*
 * BoundaryPoints.cc
 *
 * the ordered set of points on the boundary of an object
 * 
 */

#include "BoundaryPoints.h"
#include "Profile.h"
#include "SplineMatrix.h"
#include "text_output.h"
#include "SplineWeights.h"

#ifndef NO_DISPLAY
#ifdef USE_GL
#include 
#else
#include 
#include 
#include 
#endif
#endif   // #ifndef NO_DISPLAY

namespace ReadingPeopleTracker
{

// definition and initialisation of static member variables
const realno BoundaryPoints::LARGE =  1e6; // a large number

#ifndef NO_DISPLAY
#ifndef DRAW_POLY
extern NagMatrix bsplinematrix;
#endif   // ifndef DRAW_POLY
#endif   // ifndef NO_DISPLAY


void BoundaryPoints::gauss_smooth(realno sd, unsigned int window_size)
{
    realno *coeff = new realno[2 * window_size + 1];
    realno csum = 0;
    realno denom = 2 * sd * sd;
    unsigned int n,j;
    unsigned int win2 = window_size << 1;
    for (n = -window_size; n <= window_size; n++)
	csum += (coeff[n + window_size] = exp(-((realno) n * (realno) n)/denom));
    for (n = 0; n <= win2; n++)
	coeff[n] /= csum;
    
    PointVector result(no_points);
    Point2 temp;
    for (unsigned int i = 0; i < no_points; i++)
    {
	temp = Point2(0,0);
	j = (i - window_size) + no_points;
	for (n = 0; n <= win2; n++)
	    temp = temp + (coeff[n] * point_data()[j++ % no_points]);
	
	result.point_data()[i] = temp;
    }
    delete [] coeff;
    this->PointVector::operator=(result);
}

void BoundaryPoints::find_best_line(realno &inv_grad, realno &ox, realno &oy)
{
    realno sy2, sx2, sx, sy, sxy, tx, ty;
    sx = sy = sxy = sy2 = sx2 = 0.0;
    Point2 *enddata = end_data();
    for (Point2 *curr = point_data(); curr < enddata; curr++)
    {
	tx = curr->x;
	ty = curr->y;
	sx += tx;
	sy += ty;
	sx2 += (tx * tx);
	sy2 += (ty * ty);
	sxy += (tx * ty);
    }
    
    sx2 -= (sx * sx) / ((realno) no_points);
    sy2 -= (sy * sy) / ((realno) no_points);
    sxy -= (sx * sy) / ((realno) no_points);
    
    realno k = 2 * sxy / (sx2 - sy2);
    realno im1 = k / (-1 - sqrt(1 + k*k));
    realno im2 =  k  / (sqrt(1 + k*k) -1);
    realno err1 = (im1*im1*sy2 - im1*2*sxy + sx2) / (im1 * im1 + 1);
    realno err2 = (im2*im2*sy2 - im2*2*sxy + sx2) / (im2 * im2 + 1);
    if (err1 <= err2) inv_grad = im1;
    else inv_grad = im2;
    
    ox = sx / no_points;
    oy = sy / no_points;
    
}

void BoundaryPoints::find_robust_best_line(realno &inv_grad, realno &ox, realno &oy)
{
    Point2 *curr;
    // first robust center
    find_best_line(inv_grad, ox, oy);
    BoundaryPoints trimmed_pnts(no_points);
    copy(trimmed_pnts);
    for (unsigned int lp = 0; lp < 40; lp++)
    {
	realno sum_dist = 0;
	realno sum_sq_dist = 0;
//	Point2 n(inv_grad, 1);
	unsigned int np = trimmed_pnts.get_no_points();
	cdebug << " " << lp << " " << np << " (" << ox << ", " << oy << ") " << endl;
	
	for (curr = trimmed_pnts.point_data(); 
	     curr < trimmed_pnts.end_data(); curr++)     
	{
	    realno d = fabs((*curr - Point2(ox,oy)).length());
	    sum_dist += d;
	    sum_sq_dist += d * d;
	}
	realno mean_dist = sum_dist / np;
	realno sd_dist = 
	    sqrt((sum_sq_dist / np) - (mean_dist * mean_dist));
	
	realno thresh_d = mean_dist + (1.6 * sd_dist);
	BoundaryPoints new_trimmed;
	for (curr = trimmed_pnts.point_data(); 
	     curr < trimmed_pnts.end_data(); curr++)     
	{
	    realno d = fabs((*curr - Point2(ox,oy)).length());
	    if (d < thresh_d) new_trimmed.add_point(*curr);
	}
	trimmed_pnts = new_trimmed;
	if (trimmed_pnts.no_points < 4) break;
	trimmed_pnts.find_best_line(inv_grad, ox, oy);
    }
    inv_grad = 0;
}


void BoundaryPoints::find_end_highest(Point2 &n, Point2 &origin)
{
    unsigned int highest = 0;
    realno height = -1e6; 
    unsigned int i;

    for (i = 0; i < no_points; i++)
	if (point_data()[i].y > height)
	{
	    highest = i;
	    height = point_data()[i].y;
	}
    
    PointVector result(no_points);
    for (i = 0; i < no_points; i++)
	result.point_data()[i] = point_data()[(highest++ % no_points)] - origin;
    this->PointVector::operator=(result);
    anchor_point = highest; 
}



void BoundaryPoints::find_end(Point2 &n, Point2 &origin)
{
    unsigned int closest = 0, closest2 = 0;
    realno best_dist,  dist;
    realno best_dist2;
    best_dist = 1e6;
    best_dist2 = 1e6;
    unsigned int i;
    
    for (i = 0; i < no_points; i++)
    {
	Point2 p =  point_data()[i] - origin;
	dist = fabs(p^n);
	realno side = p*n;
	
	if (side >= 0)
	{
	    if (dist < best_dist)
	    {
		closest = i;
		best_dist = dist;
	    }
	}
	else
	{
	    if (dist < best_dist2)
	    {
		closest2 = i;
		best_dist2 = dist;
	    }
	}
    }
    PointVector result(no_points);
    closest2 = (closest2 - closest + no_points) % no_points;
    for (i = 0; i < no_points; i++)
	result.point_data()[i] = point_data()[(closest++ % no_points)] - origin;
    this->PointVector::operator=(result);
    anchor_point = closest2;
}

void BoundaryPoints::find_ends(realno inv_grad, realno ox, realno oy)
{
    unsigned int closest = 0, closest2 = 0;
    realno best_dist,  dist;
    realno best_dist2;
    best_dist = 1e6;
    best_dist2 = 1e6;
    unsigned int i;
    
    for (i = 0; i < no_points; i++)
    {
	dist = fabs((point_data()[i].x - ox) - 
		    inv_grad * (point_data()[i].y - oy));
	if (point_data()[i].y > oy)
	{
	    dist = (20 * dist - point_data()[i].y);
	    if (dist < best_dist)
	    {
		closest = i;
		best_dist = dist;
	    }
	}
	else
	{
	    dist = (20 * dist + point_data()[i].y);
	    if (dist < best_dist2)
	    {
		closest2 = i;
		best_dist2 = dist;
	    }
	}
    }
    PointVector result(no_points);
    Point2 centre(ox,oy);
    closest2 = (closest2 - closest + no_points) % no_points;
    for (i = 0; i < no_points; i++)
	result.point_data()[i] = point_data()[(closest++ % no_points)] - centre;
    this->PointVector::operator=(result);
    anchor_point = closest2;
}

void BoundaryPoints::recenter(realno ox, realno oy)
{
    Point2 centre(ox,oy);
    Point2 *lastpnt = end_data();
    while ((--lastpnt) >= point_data()) *lastpnt = *lastpnt - centre;
}

void BoundaryPoints::draw_points(int xlo, int ylo)
{
#ifndef NO_DISPLAY
    float vert[2];
    //color(RED);
    bgnpoint();
    Point2 *enddata = end_data();
    for (Point2 *curr = point_data(); curr < enddata; curr++)
    {
	vert[0] = curr->x + xlo;
	vert[1] = curr->y + ylo;
	v2f(vert);
    }
    endpoint();
    /*color(GREEN);
      bgnline();
      while (curr != NULL)
      {
      vert[0] = curr->dat->x + xlo;
      vert[1] = curr->dat->y + ylo;
      v2f(vert);
      curr = curr->next;
      }
      endline();*/
#endif   // #ifndef NO_DISPLAY
}

// locally optimise the parameter values associated
// with each data point to obtain the best fit
// -- this method is not guaranteed to work in general !
bool BoundaryPoints::find_optimal_spline(Profile *result)
{
    // setup initial parameter values
    filter();
    bool have_filtered = true;
    NagVector u_values(no_points);
    u_values[0] = 0.0;
    realno u_n = no_points;
    unsigned int i;
    if (!have_filtered)
	for (i = 1; i < no_points; i++)
	{
	    u_values[i] = u_values[i-1]
		+ (point_data()[i] - point_data()[i-1]).length();
	    u_n = u_values[no_points-1]
		+ (point_data()[0] - point_data()[no_points-1]).length();
	}
    else
	for (i = 1; i < no_points; i++)
	    u_values[i] = i;
    
    u_values.scale(Profile::NO_CONTROL_POINTS / u_n, u_values);
    
    // find  corresponding spline
    
    realno rms_change = 1e6;
    realno old_rms_change;
    realno curve_err = 1e6;
    realno old_curve_err;
    NagVector del_u(no_points);
    NagVector del_v(no_points);
    NagVector old_u(no_points);
    do 
    {
	u_values.copy(old_u);
	realno u_change = 0.0;
	old_curve_err = curve_err;
	curve_err = 0;
	calculate_spline(u_values, this, result);
//      result->draw(true);
	for (i = 0; i < no_points; i++)
	{
	    Point2 p, dp, ddp;
	    p.x = p.y = dp.x = dp.y = ddp.x = ddp.y = 0.0;
	    for (unsigned int j = 0; j < Profile::NO_CONTROL_POINTS; j++)
	    {
		p +=(SplineWeights::B_func(j, u_values[i]) * result->point_data()[j]);
		dp += (SplineWeights::dB_func(j, u_values[i]) *
		       result->point_data()[j]);
		ddp += (SplineWeights::ddB_func(j, u_values[i]) *
			result->point_data()[j]);
	    }
	    
// check max error too ?
	    
	    Point2 err_u = point_data()[i] - p;
	    curve_err += err_u.length2();
	    
	    realno du_max ;
	    realno du_min ;
	    du_max = 1.0 / Profile::NO_CONTROL_POINTS;
	    du_min = - du_max;
	    
	    del_u[i]  = - 1.0 * (err_u * dp) / ((err_u * ddp)  - (dp * dp));
	    
	    if (del_u[i] >= du_max)
		del_u[i] = du_max;
	    if (del_u[i] <= du_min)
		del_u[i] =  du_min;
	    
	    //if (du_max < du_min) abort();
	    
	}
	//u_change = del_u.length2();
	u_values.add(del_u, u_values);
	// make sure u_0 = 0
	for (i = 0 ; i < no_points; i++)
	    u_values[i] -= (u_values[0]);
	
	for (i = 1; i < no_points; i++)
	{
	    del_v[i-1] = u_values[i] - u_values[i-1];
	    if (del_v[i-1] < 0)
		del_v[i-1] = 0;
	}
	
	del_v[no_points-1] = u_values[0] + Profile::NO_CONTROL_POINTS -
	    u_values[no_points-1];
	
	del_v.scale(Profile::NO_CONTROL_POINTS / del_v.sum(), del_v);
	
	for (i = 1; i < no_points; i++)
	    u_values[i] = u_values[i-1] + del_v[i-1];
	
	old_u.subtract(u_values,del_u);
	
	old_rms_change = rms_change;
	u_change = del_u.length2();
	rms_change = sqrt(u_change / no_points);
//      cdebug << " rms_change " << rms_change << " curve_err " << curve_err << endl;
//      result->draw(false);
	
    } while ((fabs(rms_change - old_rms_change) > 1e-3) &&
	     (curve_err < old_curve_err));
    
    if (old_curve_err < curve_err) 
	calculate_spline(old_u, this, result);
    
    return true;
}

bool BoundaryPoints::filter()
// select data points by arc-length division 
{
    unsigned int SUB_N = 32;		
    unsigned int indx = 0;
    
    // choose a precomputed points-to-spline matrix
    // so that
    // # boundary points > # new sampled boundary points
    // but only just !
    // 
    // Hence start with RHS large and decrease until
    // condition satisfied 
    
    while ((no_points < (Profile::NO_CONTROL_POINTS * SUB_N)) && (SUB_N >= 2))
    {
	SUB_N /= 2;
	indx++;
    }
    
    // if the setup function has not been called yet, initialise
    // the required matrix
    if (s_matrices[indx] == NULL)
	s_matrices[indx] = new SplineMatrix(SUB_N);
    
    
    // pass1 - store all lengths 
    realno *arc = new realno[no_points+1];
    arc[0] = 0;
    Point2 diff;
    Point2 *input_pnts = point_data();
    unsigned int i;
    for (i = 0; i < no_points; i++)
    {
	diff = input_pnts[(i+1) % no_points] - input_pnts[i];
	arc[i+1] = arc[i] + diff.length();
    }
    
    realno total_arclength = arc[no_points];
    
    // pass2 - select relevant points
    
    // set the spacing interval for parametric values
    realno interval = arc[no_points] / ((realno)(Profile::NO_CONTROL_POINTS * SUB_N));
    
    unsigned int curr_pos = 1;
    unsigned int new_no_points = Profile::NO_CONTROL_POINTS * SUB_N;
    
    BoundaryPoints result(new_no_points);
    result.point_data()[0] = point_data()[0];
    realno l1,l2, iarc;
    for (i = 1; i < new_no_points; i++)
    {
	iarc = ((realno)i) * interval;
	
	// skip points if necessary
	while (arc[curr_pos] < iarc)
	    curr_pos++;
	
	if (curr_pos > no_points)
	{
	    cerror << " Error in BoundaryPoints::filter(): no matching points "
		   << endl;
	    
	    exit(1);
	}
	
	// use linear interpolation of lengths to define new point
	l1 = arc[curr_pos-1]; 
	l2 = arc[(curr_pos) % no_points];
	// check for gone full circle
	if (curr_pos == no_points) l2 += total_arclength;
	
	realno lmda = (iarc - l1) / (l2 - l1);
	
	result.point_data()[i] = point_data()[curr_pos-1] * (1.0 - lmda) 
	    + point_data()[(curr_pos) % no_points] * lmda;
	
    }
    delete [] arc;
    *this = result;
    return true;
}


bool BoundaryPoints::convert_to_spline(PointVector *result)
{
    if (filter() == false)
	return false;

    to_spline(result);

    return true;
}

void BoundaryPoints::to_spline(PointVector *result)
{
    if (result->get_no_points() < Profile::NO_CONTROL_POINTS)
    {
	cerror << " Error in BoundaryPoints::to_spline(): too many control points requested \n"
	       << endl;
	
	exit(1);
    }
    
    // get the relevant points-to-spline matrix 
    // (again !!)
    unsigned int SUB_N = no_points / Profile::NO_CONTROL_POINTS;
    unsigned int temp_n = SUB_N;
    unsigned int indx = 0;
    
    while (temp_n < 32)
	temp_n *= 2; indx++;
    
    if (s_matrices[indx] == NULL)
	s_matrices[indx] = new SplineMatrix(SUB_N);
    
    SplineMatrix *s_matrix = s_matrices[indx];
    
    // calculate the RHS of the simultaneous equations
    realno *vdat = s_matrix->vdata;
    unsigned int i; unsigned int j; unsigned int l; unsigned int i2; 
    NagVector rhsx(Profile::NO_CONTROL_POINTS);
    NagVector rhsy(Profile::NO_CONTROL_POINTS);
    unsigned int ntotal = SUB_N * Profile::NO_CONTROL_POINTS;
    realno temp; 
    // get the corresponding vectors 
    for (l = 0; l < Profile::NO_CONTROL_POINTS; l++)
    {
	rhsx.get_data()[l] = 0;
	rhsy.get_data()[l] = 0;
	j = 0;
	for (i = (l - 2) * SUB_N;i <= (l + 2) * SUB_N; i++)
	{
	    i2 = (i + ntotal) % ntotal;
	    temp = vdat[j++];
	    rhsx.get_data()[l] += temp * (point_data()[i2].x);
	    rhsy.get_data()[l] += temp * (point_data()[i2].y);
	}
    }
    NagVector resx(Profile::NO_CONTROL_POINTS);
    NagVector resy(Profile::NO_CONTROL_POINTS);
    s_matrix->multiply(rhsx,resx);
    s_matrix->multiply(rhsy,resy);
    Point2 *spln = result->point_data();
    for (l = 0; l < Profile::NO_CONTROL_POINTS; l++)
	*spln++ = Point2(resx[l],resy[l]);
}


void BoundaryPoints::draw_curve(int ox, int oy, int step)
{
#ifndef NO_DISPLAY
#ifndef DISPLAY_POLY
    defbasis(1,bsplinematrix);
    curveprecision(15);
    linewidth(2);
    Coord geom1[4][3];
    unsigned int i,j;
    geom1[0][2] = geom1[1][2] = geom1[2][2] = geom1[3][2] = 0;
    //char test_str[50];
    for (i = 0; i < no_points; i += step)
    {
	geom1[0][0] = point_data()[i].x + ox;
	geom1[0][1] = point_data()[i].y + oy;
	j = (i+step) % no_points;
	geom1[1][0] = point_data()[j].x + ox;
	geom1[1][1] = point_data()[j].y + oy;
	j = (j+step) % no_points;
	geom1[2][0] = point_data()[j].x + ox;
	geom1[2][1] = point_data()[j].y + oy;
	j = (j+step) % no_points;
	geom1[3][0] = point_data()[j].x + ox;
	geom1[3][1] = point_data()[j].y + oy;
	crv(geom1);
    }
#endif   // ifndef DISPLAY_POLY
#endif   // ifndef NO_DISPLAY
}


//////////////////////////////////////////////////////////////////////////
//									//
//  alternative methods ....						//
//									//
//////////////////////////////////////////////////////////////////////////


// calculate a spline from an arbitrary set of Boundary points
// without worrying about speed / efficiency
void BoundaryPoints::calculate_spline(NagVector &u_values, PointVector *data,
				      PointVector *result)
{
    NagMatrix M(Profile::NO_CONTROL_POINTS, Profile::NO_CONTROL_POINTS);
    M.clear(0);
    unsigned int n = u_values.get_size();
    unsigned int i;
    unsigned int j;
    unsigned int k;
    
    for (j = 0; j < Profile::NO_CONTROL_POINTS; j++)
	for (k = 0; k < Profile::NO_CONTROL_POINTS; k++)
	    for (i = 0; i < n; i++)
		*M.get(j,k) += SplineWeights::B_func(j, u_values[i]) *
		    SplineWeights::B_func(k,u_values[i]);
    
    
    NagMatrix B(Profile::NO_CONTROL_POINTS, 2);
    NagVector rhs_x(Profile::NO_CONTROL_POINTS, B.get(0,0));
    NagVector rhs_y(Profile::NO_CONTROL_POINTS, B.get(0,1));
    
    rhs_x.clear(0);
    rhs_y.clear(0);
    
    
    for (j = 0; j < Profile::NO_CONTROL_POINTS; j++)
	for (i = 0; i < n; i++)
	{
	    realno tmp = SplineWeights::B_func(j, u_values[i]);
	    rhs_x[j] += tmp * data->point_data()[i].x;
	    rhs_y[j] += tmp * data->point_data()[i].y;
	}
    rhs_x.reset();
    rhs_y.reset();
    
    NagMatrix C;
    M.solve_equations(B, C);
    
    for (j = 0; j < Profile::NO_CONTROL_POINTS; j++)
	result->point_data()[j] = Point2(C.read(j,0), C.read(j, 1));
}


void BoundaryPoints::setup_default_spline_matrices()
{
    cdebug << " BoundaryPoints: setting up default spline matrices ..." << endl;

    s_matrices[0] = new SplineMatrix(32);
    s_matrices[1] = new SplineMatrix(16);
    s_matrices[2] = new SplineMatrix(8);
    s_matrices[3] = new SplineMatrix(4);
    s_matrices[4] = new SplineMatrix(2);
    s_matrices[5] = new SplineMatrix(1);
    
}

} // namespace ReadingPeopleTracker