示例#1
0
bool RegistrationTools::RegistrationProcedure(GenericCloud* P,
											  GenericCloud* X,
											  PointProjectionTools::Transformation& trans,
											  ScalarField* weightsP/*=0*/,
											  ScalarField* weightsX/*=0*/,
											  PointCoordinateType scale/*=1.0f*/)
{
    //resulting transformation (R is invalid on initialization and T is (0,0,0))
    trans.R.invalidate();
    trans.T = CCVector3(0,0,0);

	PointCoordinateType bbMin[3],bbMax[3];
	X->getBoundingBox(bbMin,bbMax);

	PointCoordinateType dx = bbMax[0]-bbMin[0];
	PointCoordinateType dy = bbMax[1]-bbMin[1];
	PointCoordinateType dz = bbMax[2]-bbMin[2];

	CCVector3 Gp = GeometricalAnalysisTools::computeGravityCenter(P);
	CCVector3 Gx = GeometricalAnalysisTools::computeGravityCenter(X);

	//if the cloud is equivalent to a single point (for instance
	//it's the case when the two clouds are very far away from 
	//each other in the ICP process) we try to get the two clouds closer
	if (fabs(dx)+fabs(dy)+fabs(dz) < ZERO_TOLERANCE)
	{
	    trans.T = Gx - Gp*scale;
        return true;
	}

	//Cross covariance matrix
	SquareMatrixd Sigma_px = (weightsP || weightsX) ? GeometricalAnalysisTools::computeWeightedCrossCovarianceMatrix(P,X,Gp.u,Gx.u,weightsP,weightsX) : GeometricalAnalysisTools::computeCrossCovarianceMatrix(P,X,Gp.u,Gx.u);
	if (!Sigma_px.isValid())
		return false;

	SquareMatrixd Sigma_px_t = Sigma_px;
	Sigma_px_t.transpose();

	SquareMatrixd Aij = Sigma_px-Sigma_px_t;

	double trace = Sigma_px.trace();

	SquareMatrixd traceI3(3);
	traceI3.m_values[0][0]=trace;
	traceI3.m_values[1][1]=trace;
	traceI3.m_values[2][2]=trace;

	SquareMatrixd bottomMat = Sigma_px+Sigma_px_t-traceI3;

    //we build up the registration matrix (see ICP algorithm)
	SquareMatrixd QSigma(4);

	QSigma.m_values[0][0]=trace;

	QSigma.m_values[0][1]=QSigma.m_values[1][0]=Aij.m_values[1][2];
	QSigma.m_values[0][2]=QSigma.m_values[2][0]=Aij.m_values[2][0];
	QSigma.m_values[0][3]=QSigma.m_values[3][0]=Aij.m_values[0][1];

	QSigma.m_values[1][1]=bottomMat.m_values[0][0];
	QSigma.m_values[1][2]=bottomMat.m_values[0][1];
	QSigma.m_values[1][3]=bottomMat.m_values[0][2];

	QSigma.m_values[2][1]=bottomMat.m_values[1][0];
	QSigma.m_values[2][2]=bottomMat.m_values[1][1];
	QSigma.m_values[2][3]=bottomMat.m_values[1][2];

	QSigma.m_values[3][1]=bottomMat.m_values[2][0];
	QSigma.m_values[3][2]=bottomMat.m_values[2][1];
	QSigma.m_values[3][3]=bottomMat.m_values[2][2];

    //we compute its eigenvalues and eigenvectors
	SquareMatrixd eig = QSigma.computeJacobianEigenValuesAndVectors();
	if (!eig.isValid())
        return false;

	//as Besl says, the best rotation corresponds to the eigenvector associated to the biggest eigenvalue
    double qR[4];
	eig.getMaxEigenValueAndVector(qR);

    //these eigenvalue and eigenvector correspond to a quaternion --> we get the corresponding matrix
	trans.R.initFromQuaternion(qR);

    //and we deduce the translation
	trans.T = Gx - (trans.R*Gp)*scale;

	return true;
}
示例#2
0
bool Neighbourhood::compute3DQuadric(double quadricEquation[10])
{
	if (!m_associatedCloud || !quadricEquation)
	{
		//invalid (input) parameters
		assert(false);
		return false;
	}

	//computes a 3D quadric of the form ax2 +by2 +cz2 + 2exy + 2fyz + 2gzx + 2lx + 2my + 2nz + d = 0
	//"THREE-DIMENSIONAL SURFACE CURVATURE ESTIMATION USING QUADRIC SURFACE PATCHES", I. Douros & B. Buxton, University College London

	//we get centroid
	const CCVector3* G = getGravityCenter();
	assert(G);

	//we look for the eigen vector associated to the minimum eigen value of a matrix A
	//where A=transpose(D)*D, and D=[xi^2 yi^2 zi^2 xiyi yizi xizi xi yi zi 1] (i=1..N)

	unsigned count = m_associatedCloud->size();

	//we compute M = [x2 y2 z2 xy yz xz x y z 1] for all points
	std::vector<PointCoordinateType> M;
	{
		try
		{
			M.resize(count*10);
		}
		catch (std::bad_alloc)
		{
			return false;
		}

		PointCoordinateType* _M = &(M[0]);
		for (unsigned i=0; i<count; ++i)
		{
			CCVector3 P = *m_associatedCloud->getPoint(i) - *G;

			//we fill the ith line
			(*_M++) = P.x * P.x;
			(*_M++) = P.y * P.y;
			(*_M++) = P.z * P.z;
			(*_M++) = P.x * P.y;
			(*_M++) = P.y * P.z;
			(*_M++) = P.x * P.z;
			(*_M++) = P.x;
			(*_M++) = P.y;
			(*_M++) = P.z;
			(*_M++) = 1;
		}
	}

	//D = tM.M
	SquareMatrixd D(10);
	for (unsigned l=0; l<10; ++l)
	{
		for (unsigned c=0; c<10; ++c)
		{
			double sum = 0;
			PointCoordinateType* _M = &(M[0]);
			for (unsigned i=0; i<count; ++i, _M+=10)
				sum += static_cast<double>(_M[l] * _M[c]);

			D.m_values[l][c] = sum;
		}
	}

	//we don't need M anymore
	M.clear();

	//now we compute eigen values and vectors of D
	SquareMatrixd eig = D.computeJacobianEigenValuesAndVectors();
	//failure?
	if (!eig.isValid())
		return false;

	//we get the eigen vector corresponding to the minimum eigen value
	/*double lambdaMin = */eig.getMinEigenValueAndVector(quadricEquation);

	return true;
}
示例#3
0
bool Neighbourhood::compute3DQuadric()
{
	//invalidate previous quadric (if any)
	structuresValidity &= (~QUADRIC_3D);

	assert(m_associatedCloud);
	if (!m_associatedCloud)
		return false;

    //computes a 3D quadric of the form ax2 +by2 +cz2 + 2exy + 2fyz + 2gzx + 2lx + 2my + 2nz + d = 0
    //"THREE-DIMENSIONAL SURFACE CURVATURE ESTIMATION USING QUADRIC SURFACE PATCHES", I. Douros & B. Buxton, University College London

	//we get centroid
	const CCVector3* G = getGravityCenter();
	assert(G);

    //we look for the eigen vector associated to the minimum eigen value of a matrix A
    //where A=transpose(D)*D, and D=[xi^2 yi^2 zi^2 xiyi yizi xizi xi yi zi 1] (i=1..N)

	unsigned i,l,c,count = m_associatedCloud->size();
	CCVector3 P;

    //we compute M = [x2 y2 z2 xy yz xz x y z 1] for all points
    PointCoordinateType* M = new PointCoordinateType[count*10];
    if (!M)
        return false;

	PointCoordinateType* _M = M;
	for (i=0;i<count;++i)
	{
		P = *m_associatedCloud->getPoint(i) - *G;

        //we fill the ith line
	    (*_M++) = P.x * P.x;
	    (*_M++) = P.y * P.y;
	    (*_M++) = P.z * P.z;
	    (*_M++) = P.x * P.y;
	    (*_M++) = P.y * P.z;
	    (*_M++) = P.x * P.z;
	    (*_M++) = P.x;
	    (*_M++) = P.y;
	    (*_M++) = P.z;
	    (*_M++) = 1.0;
	}

    //D = tM.M
    SquareMatrixd D(10);
	for (l=0; l<10; ++l)
	{
        for (c=0; c<10; ++c)
        {
            double sum=0.0;
            _M = M;
            for (i=0; i<count; ++i)
            {
                sum += (double)(_M[l] * _M[c]);
                _M+=10;
            }

            D.m_values[l][c] = sum;
        }
	}

    //we don't need M anymore
	delete[] M;
	M=0;

    //now we compute eigen values and vectors of D
	SquareMatrixd eig = D.computeJacobianEigenValuesAndVectors();
    //failure?
	if (!eig.isValid())
		return false;

	//we get the eigen vector corresponding to the minimum eigen value
	double vec[10];
	/*double lambdaMin = */eig.getMinEigenValueAndVector(vec);

    //we store result
	for (i=0;i<10;++i)
        the3DQuadric[i]=vec[i];

	structuresValidity |= QUADRIC_3D;

	return true;
}
示例#4
0
ScalarType Neighbourhood::computeCurvature2(unsigned neighbourIndex, CC_CURVATURE_TYPE cType)
{
	//we get 3D quadric parameters
	const double* Q = get3DQuadric();
	if (!Q)
        return NAN_VALUE;

	//we get centroid (should have already been computed during Quadric computation)
	const CCVector3* Gc = getGravityCenter();

    //we compute curvature at the input neighbour position + we recenter it by the way
	CCVector3 Pc = *m_associatedCloud->getPoint(neighbourIndex) - *Gc;

    double a=Q[0];
    const double b=Q[1]/a;
    const double c=Q[2]/a;
    const double e=Q[3]/a;
    const double f=Q[4]/a;
    const double g=Q[5]/a;
    const double l=Q[6]/a;
    const double m=Q[7]/a;
    const double n=Q[8]/a;
    //const double d=Q[9]/a;
    a=1.0;

    const double& x=Pc.x;
    const double& y=Pc.y;
    const double& z=Pc.z;

    //first order partial derivatives
    const double Fx = 2.*a*x+e*y+g*z+l;
    const double Fy = 2.*b*y+e*x+f*z+m;
    const double Fz = 2.*c*z+f*y+g*x+n;

    const double Fx2 = Fx*Fx;
    const double Fy2 = Fy*Fy;
    const double Fz2 = Fz*Fz;

    //coefficients E,F,G
    const double E = 1.+Fx2/Fz2;
    const double F = Fx2/Fz2;
    const double G = 1.+Fy2/Fz2;

    //second order partial derivatives
    //const double Fxx = 2.*a;
    //const double Fyy = 2.*b;
    //const double Fzz = 2.*c;
    //const double Fxy = e;
    //const double Fyx = e;
    //const double Fyz = f;
    //const double Fzy = f;
    //const double Fxz = g;
    //const double Fzx = g;

    //gradient norm
    const double gradF = sqrt(Fx2+Fy2+Fz2);

    //coefficients L,M,N
    SquareMatrixd D(3);
    D.m_values[0][0] = 2.*a;//Fxx;
    D.m_values[0][1] = g;//Fxz;
    D.m_values[0][2] = Fx;
    D.m_values[1][0] = g;//Fzx;
    D.m_values[1][1] = 2.*c;//Fzz;
    D.m_values[1][2] = Fz;
    D.m_values[2][0] = Fx;
    D.m_values[2][1] = Fz;
    D.m_values[2][2] = 0.0;
    const double L = D.computeDet()/(Fz2*gradF);

    D.m_values[0][0] = e;//Fxy;
    D.m_values[0][1] = f;//Fyz;
    D.m_values[0][2] = Fy;
    /*D.m_values[1][0] = Fzx;
    D.m_values[1][1] = Fzz;
    D.m_values[1][2] = Fz;
    D.m_values[2][0] = Fx;
    D.m_values[2][1] = Fz;
    D.m_values[2][2] = 0.0;
    //*/
    const double M = D.computeDet()/(Fz2*gradF);

      D.m_values[0][0] = 2.*b;//Fyy;
    //D.m_values[0][1] = f;//Fyz;
    //D.m_values[0][2] = Fy;
      D.m_values[1][0] = f;//Fzy;
    //D.m_values[1][1] = Fzz;
    //D.m_values[1][2] = Fz;
      D.m_values[2][0] = Fy;
    //D.m_values[2][1] = Fz;
    //D.m_values[2][2] = 0.0;
    const double N = D.computeDet()/(Fz2*gradF);

    //compute curvature
    SquareMatrixd A(2);
    A.m_values[0][0] = L;
    A.m_values[0][1] = M;
    A.m_values[1][0] = M;
    A.m_values[1][1] = N;

    SquareMatrixd B(2);
    B.m_values[0][0] = E;
    B.m_values[0][1] = F;
    B.m_values[1][0] = F;
    B.m_values[1][1] = G;

    /*FILE* fp = fopen("computeCurvature2_trace.txt","wt");
    fprintf(fp,"Fx=%f\n",Fx);
    fprintf(fp,"Fy=%f\n",Fx);
    fprintf(fp,"Fz=%f\n",Fx);
    fprintf(fp,"E=%f\n",E);
    fprintf(fp,"F=%f\n",F);
    fprintf(fp,"G=%f\n",G);
    fprintf(fp,"L=%f\n",L);
    fprintf(fp,"M=%f\n",M);
    fprintf(fp,"N=%f\n",N);
    fprintf(fp,"A:\n%f %f\n%f %f\n",A.m_values[0][0],A.m_values[0][1],A.m_values[1][0],A.m_values[1][1]);
    fprintf(fp,"B:\n%f %f\n%f %f\n",B.m_values[0][0],B.m_values[0][1],B.m_values[1][0],B.m_values[1][1]);
    //fclose(fp);
    //*/

    //Gaussian curvature K = det(A)/det(B)
    if (cType==GAUSSIAN_CURV)
    {
        ScalarType K = (ScalarType)(A.computeDet() / B.computeDet());
        if (K<-1.0)
            K=-1.0;
        else if (K>1.0)
            K=1.0;
        return K;
    }
    //*/

    SquareMatrixd Binv = B.inv();
    if (!Binv.isValid())
        return NAN_VALUE;

    SquareMatrixd C = B.inv() * A;

    //Mean curvature H = 1/2 trace(B^-1 * A)
    if (cType==MEAN_CURV)
    {
        ScalarType H = (ScalarType)(0.5*C.trace());
        if (H<-1.0)
            H=-1.0;
        else if (H>1.0)
            H=1.0;
        return fabs(H);
    }
    //*/

    //principal curvatures are the eigenvalues k1 and k2 of B^-1 * A
    /*SquareMatrixd eig = C.computeJacobianEigenValuesAndVectors();
	if (!eig.isValid())
		return NAN_VALUE;

    const double k1 = eig.getEigenValueAndVector(0);
    const double k2 = eig.getEigenValueAndVector(1);
    //*/

    /*FILE* fp = fopen("computeCurvature2_trace.txt","a");
    fprintf(fp,"Binv:\n%f %f\n%f %f\n",Binv.values[0][0],Binv.values[0][1],Binv.values[1][0],Binv.values[1][1]);
    fprintf(fp,"C:\n%f %f\n%f %f\n",C.values[0][0],C.values[0][1],C.values[1][0],C.values[1][1]);
    fprintf(fp,"k1=%f\n",k1);
    fprintf(fp,"k2=%f\n",k2);
    fclose(fp);
    //*/

    /*switch (cType)
    {
        case GAUSSIAN_CURV:
            //Gaussian curvature
            return k1*k2;
        case MEAN_CURV:
            //Mean curvature
            return (k1+k2)/2.0;
    }
    //*/

    return NAN_VALUE;
}
示例#5
0
bool RegistrationTools::RegistrationProcedure(GenericCloud* P,
                                              GenericCloud* X,
                                              ScaledTransformation& trans,
											  bool adjustScale/*=false*/,
                                              ScalarField* weightsP/*=0*/,
                                              ScalarField* weightsX/*=0*/,
                                              PointCoordinateType aPrioriScale/*=1.0f*/)
{
    //resulting transformation (R is invalid on initialization, T is (0,0,0) and s==1)
    trans.R.invalidate();
    trans.T = CCVector3(0,0,0);
	trans.s = PC_ONE;

	if (P == 0 || X == 0 || P->size() != X->size() || P->size() < 3)
		return false;

    PointCoordinateType bbMin[3],bbMax[3];
    X->getBoundingBox(bbMin,bbMax);

    //BBox dimensions
    PointCoordinateType dx = bbMax[0]-bbMin[0];
    PointCoordinateType dy = bbMax[1]-bbMin[1];
    PointCoordinateType dz = bbMax[2]-bbMin[2];

    //centers of mass
    CCVector3 Gp = GeometricalAnalysisTools::computeGravityCenter(P);
    CCVector3 Gx = GeometricalAnalysisTools::computeGravityCenter(X);

    //if the data cloud is equivalent to a single point (for instance
    //it's the case when the two clouds are very far away from
    //each other in the ICP process) we try to get the two clouds closer
    if (fabs(dx)+fabs(dy)+fabs(dz) < ZERO_TOLERANCE)
    {
        trans.T = Gx - Gp*aPrioriScale;
        return true;
    }

    //Cross covariance matrix, eq #24 in Besl92 (but with weights, if any)
    SquareMatrixd Sigma_px = (weightsP || weightsX) ? GeometricalAnalysisTools::computeWeightedCrossCovarianceMatrix(P,X,Gp.u,Gx.u,weightsP,weightsX) : GeometricalAnalysisTools::computeCrossCovarianceMatrix(P,X,Gp.u,Gx.u);
    if (!Sigma_px.isValid())
        return false;

    SquareMatrixd Sigma_px_t = Sigma_px; //sigma_px_t is sigma_px transposed!
    Sigma_px_t.transpose();

    SquareMatrixd Aij = Sigma_px-Sigma_px_t;

    double trace = Sigma_px.trace(); //that is the sum of diagonal elements f sigma_px

    SquareMatrixd traceI3(3); //create the I matrix with eigvals equal to trace
    traceI3.m_values[0][0]=trace;
    traceI3.m_values[1][1]=trace;
    traceI3.m_values[2][2]=trace;

    SquareMatrixd bottomMat = Sigma_px+Sigma_px_t-traceI3;

    //we build up the registration matrix (see ICP algorithm)
    SquareMatrixd QSigma(4); //#25 in the paper (besl)

    QSigma.m_values[0][0] = trace;
						    
    QSigma.m_values[0][1] = QSigma.m_values[1][0] = Aij.m_values[1][2];
    QSigma.m_values[0][2] = QSigma.m_values[2][0] = Aij.m_values[2][0];
    QSigma.m_values[0][3] = QSigma.m_values[3][0] = Aij.m_values[0][1];
						    
    QSigma.m_values[1][1] = bottomMat.m_values[0][0];
    QSigma.m_values[1][2] = bottomMat.m_values[0][1];
    QSigma.m_values[1][3] = bottomMat.m_values[0][2];
						    
    QSigma.m_values[2][1] = bottomMat.m_values[1][0];
    QSigma.m_values[2][2] = bottomMat.m_values[1][1];
    QSigma.m_values[2][3] = bottomMat.m_values[1][2];
						    
    QSigma.m_values[3][1] = bottomMat.m_values[2][0];
    QSigma.m_values[3][2] = bottomMat.m_values[2][1];
    QSigma.m_values[3][3] = bottomMat.m_values[2][2];

    //we compute its eigenvalues and eigenvectors
    SquareMatrixd eig = QSigma.computeJacobianEigenValuesAndVectors();

    if (!eig.isValid())
        return false;

    //as Besl says, the best rotation corresponds to the eigenvector associated to the biggest eigenvalue
    double qR[4];
    eig.getMaxEigenValueAndVector(qR);

    //these eigenvalue and eigenvector correspond to a quaternion --> we get the corresponding matrix
    trans.R.initFromQuaternion(qR);

	if (adjustScale)
	{
		//two accumulators
		double acc_num = 0.0;
		double acc_denom = 0.0;

		//now deduce the scale (refer to "Point Set Registration with Integrated Scale Estimation", Zinsser et. al, PRIP 2005)
		X->placeIteratorAtBegining();
		P->placeIteratorAtBegining();

		unsigned count = X->size();
		assert(P->size() == count);
		for (unsigned i=0; i<count; ++i)
		{
			//'a' refers to the data 'A' (moving) = P
			//'b' refers to the model 'B' (not moving) = X
			CCVector3 a_tilde = trans.R * (*(P->getNextPoint()) - Gp);	// a_tilde_i = R * (a_i - a_mean)
			CCVector3 b_tilde = (*(X->getNextPoint()) - Gx);			// b_tilde_j =     (b_j - b_mean)

			acc_num += (double)b_tilde.dot(a_tilde);
			acc_denom += (double)a_tilde.dot(a_tilde);
		}

		//DGM: acc_2 can't be 0 because we already have checked that the bbox is not a single point!
		assert(acc_denom > 0.0);
		trans.s = static_cast<PointCoordinateType>(fabs(acc_num / acc_denom));
	}

    //and we deduce the translation
    trans.T = Gx - (trans.R*Gp)*(aPrioriScale*trans.s); //#26 in besl paper, modified with the scale as in jschmidt

    return true;
}