Beispiel #1
0
ScalarType Neighbourhood::computeCurvature(unsigned neighbourIndex, CC_CURVATURE_TYPE cType)
{
	switch (cType)
	{
	case GAUSSIAN_CURV:
	case MEAN_CURV:
		{
			//we get 2D1/2 quadric parameters
			const PointCoordinateType* H = getQuadric();
			if (!H)
				return NAN_VALUE;

			//compute centroid
			const CCVector3* G = getGravityCenter();

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

			const uchar X = m_quadricEquationDirections.x;
			const uchar Y = m_quadricEquationDirections.y;

			//z = a+b.x+c.y+d.x^2+e.x.y+f.y^2
			//const PointCoordinateType& a = H[0];
			const PointCoordinateType& b = H[1];
			const PointCoordinateType& c = H[2];
			const PointCoordinateType& d = H[3];
			const PointCoordinateType& e = H[4];
			const PointCoordinateType& f = H[5];

			//See "CURVATURE OF CURVES AND SURFACES – A PARABOLIC APPROACH" by ZVI HAR’EL
			const PointCoordinateType  fx	= b + (d*2) * Q.u[X] + (e  ) * Q.u[Y];	// b+2d*X+eY
			const PointCoordinateType  fy	= c + (e  ) * Q.u[X] + (f*2) * Q.u[Y];	// c+2f*Y+eX
			const PointCoordinateType  fxx	= d*2;									// 2d
			const PointCoordinateType  fyy	= f*2;									// 2f
			const PointCoordinateType& fxy	= e;									// e

			const PointCoordinateType fx2 = fx*fx;
			const PointCoordinateType fy2 = fy*fy;
			const PointCoordinateType q = (1 + fx2 + fy2);

			switch (cType)
			{
			case GAUSSIAN_CURV:
				{
					//to sign the curvature, we need a normal!
					PointCoordinateType K = fabs( fxx*fyy - fxy*fxy ) / (q*q);
					return static_cast<ScalarType>(K);
				}

			case MEAN_CURV:
				{
					//to sign the curvature, we need a normal!
					PointCoordinateType H = fabs( ((1+fx2)*fyy - 2*fx*fy*fxy + (1+fy2)*fxx) ) / (2*sqrt(q)*q);
					return static_cast<ScalarType>(H);
				}

			default:
				assert(false);
			}
		}
		break;

	case NORMAL_CHANGE_RATE:
		{
			assert(m_associatedCloud);
			unsigned pointCount = (m_associatedCloud ? m_associatedCloud->size() : 0);

			//we need at least 4 points
			if (pointCount < 4)
			{
				//not enough points!
				return pointCount == 3 ? 0 : NAN_VALUE;
			}

			//we determine plane normal by computing the smallest eigen value of M = 1/n * S[(p-µ)*(p-µ)']
			CCLib::SquareMatrixd eig = computeCovarianceMatrix().computeJacobianEigenValuesAndVectors();

			//invalid matrix?
			if (!eig.isValid())
				return NAN_VALUE;

			//compute curvature as the rate of change of the surface
			double e0 = eig.getEigenValues()[0];
			double e1 = eig.getEigenValues()[1];
			double e2 = eig.getEigenValues()[2];
			double  sum = fabs(e0+e1+e2);
			if (sum < ZERO_TOLERANCE)
				return NAN_VALUE;

			double eMin = std::min(std::min(e0,e1),e2);
			return static_cast<ScalarType>(fabs(eMin) / sum);
		}
		break;

	default:
		assert(false);
	}

	return NAN_VALUE;
}
ccPlane* ccGenericPointCloud::fitPlane(double* rms /*= 0*/)
{
	//number of points
	unsigned count = size();
	if (count<3)
		return 0;

	CCLib::Neighbourhood Yk(this);

	//we determine plane normal by computing the smallest eigen value of M = 1/n * S[(p-µ)*(p-µ)']
	CCLib::SquareMatrixd eig = Yk.computeCovarianceMatrix().computeJacobianEigenValuesAndVectors();

	//invalid matrix?
	if (!eig.isValid())
	{
		//ccConsole::Warning("[ccPointCloud::fitPlane] Failed to compute plane/normal for cloud '%s'",getName());
		return 0;
	}
	eig.sortEigenValuesAndVectors();

	//plane equation
	PointCoordinateType theLSQPlane[4];

	//the smallest eigen vector corresponds to the "least square best fitting plane" normal
	double vec[3];
	eig.getEigenValueAndVector(2,vec);
	//PointCoordinateType sign = (vec[2] < 0.0 ? -1.0 : 1.0);  //plane normal (always with a positive 'Z' by default)
	for (unsigned i=0;i<3;++i)
		theLSQPlane[i]=/*sign*/(PointCoordinateType)vec[i];
	CCVector3 N(theLSQPlane);

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

	//eventually we just have to compute 'constant' coefficient a3
	//we use the fact that the plane pass through the centroid --> GM.N = 0 (scalar prod)
	//i.e. a0*G[0]+a1*G[1]+a2*G[2]=a3
	theLSQPlane[3] =  G->dot(N);

	//least-square fitting RMS
	if (rms)
	{
		placeIteratorAtBegining();
		*rms = 0.0;
		for (unsigned k=0;k<count;++k)
		{
			double d = (double)CCLib::DistanceComputationTools::computePoint2PlaneDistance(getNextPoint(),theLSQPlane);
			*rms += d*d;
		}
		*rms = sqrt(*rms)/(double)count;
	}

	//we has a plane primitive to the cloud
	eig.getEigenValueAndVector(0,vec); //main direction
	CCVector3 X(vec[0],vec[1],vec[2]); //plane normal
	//eig.getEigenValueAndVector(1,vec); //intermediate direction
	//CCVector3 Y(vec[0],vec[1],vec[2]); //plane normal
	CCVector3 Y = N * X;

	//we eventually check for plane extents
	PointCoordinateType minX=0.0,maxX=0.0,minY=0.0,maxY=0.0;
	placeIteratorAtBegining();
	for (unsigned k=0;k<count;++k)
	{
		//projetion into local 2D plane ref.
		CCVector3 P = *getNextPoint() - *G;
		PointCoordinateType x2D = P.dot(X);
		PointCoordinateType y2D = P.dot(Y);

		if (k!=0)
		{
			if (minX<x2D)
				minX=x2D;
			else if (maxX>x2D)
				maxX=x2D;
			if (minY<y2D)
				minY=y2D;
			else if (maxY>y2D)
				maxY=y2D;
		}
		else
		{
			minX=maxX=x2D;
			minY=maxY=y2D;
		}
	}

	//we recenter plane (as it is not always the case!)
	float dX = maxX-minX;
	float dY = maxY-minY;
	CCVector3 Gt = *G + X * (minX+dX*0.5);
	Gt += Y * (minY+dY*0.5);
	ccGLMatrix glMat(X,Y,N,Gt);

	return new ccPlane(dX,dY,&glMat);
}
Beispiel #3
0
bool Neighbourhood::computeLeastSquareBestFittingPlane()
{
	//invalidate previous LS plane (if any)
	m_structuresValidity &= (~FLAG_LS_PLANE);

	assert(m_associatedCloud);
	unsigned pointCount = (m_associatedCloud ? m_associatedCloud->size() : 0);

	//we need at least 3 points to compute a plane
	assert(CC_LOCAL_MODEL_MIN_SIZE[LS] >= 3);
	if (pointCount < CC_LOCAL_MODEL_MIN_SIZE[LS])
	{
		//not enough points!
		return false;
	}

	CCVector3 G(0,0,0);
	if (pointCount > 3)
	{
		//we determine plane normal by computing the smallest eigen value of M = 1/n * S[(p-µ)*(p-µ)']
		CCLib::SquareMatrixd eig = computeCovarianceMatrix().computeJacobianEigenValuesAndVectors();

		//invalid matrix?
		if (!eig.isValid())
			return false;

		//get normal
		{
			CCVector3d vec;
			//the smallest eigen vector corresponds to the "least square best fitting plane" normal
			eig.getMinEigenValueAndVector(vec.u);
			m_lsPlaneVectors[2] = CCVector3::fromArray(vec.u);
		}

		//get also X (Y will be deduced by cross product, see below
		{
			CCVector3d vec;
			eig.getMaxEigenValueAndVector(vec.u);
			m_lsPlaneVectors[0] = CCVector3::fromArray(vec.u);
		}

		//get the centroid (should already be up-to-date - see computeCovarianceMatrix)
		G = *getGravityCenter();
	}
	else
	{
		//we simply compute the normal of the 3 points by cross product!
		const CCVector3* A = m_associatedCloud->getPoint(0);
		const CCVector3* B = m_associatedCloud->getPoint(1);
		const CCVector3* C = m_associatedCloud->getPoint(2);

		//get X (AB by default) and Y (AC - will be updated later) and deduce N = X ^ Y
		m_lsPlaneVectors[0] = (*B-*A);
		m_lsPlaneVectors[1] = (*C-*A);
		m_lsPlaneVectors[2] = m_lsPlaneVectors[0].cross(m_lsPlaneVectors[1]);

		//the plane passes through any of the 3 points
		G = *A;
	}

	//make sure all vectors are unit!
	if (m_lsPlaneVectors[2].norm2() < ZERO_TOLERANCE)
	{
		//this means that the points are colinear!
		//m_lsPlaneVectors[2] = CCVector3(0,0,1); //any normal will do
		return false;
	}
	else
	{
		m_lsPlaneVectors[2].normalize();
	}
	//normalize X as well
	m_lsPlaneVectors[0].normalize();
	//and update Y
	m_lsPlaneVectors[1] = m_lsPlaneVectors[2].cross(m_lsPlaneVectors[0]);

	//deduce the proper equation
	m_lsPlaneEquation[0] = m_lsPlaneVectors[2].x;
	m_lsPlaneEquation[1] = m_lsPlaneVectors[2].y;
	m_lsPlaneEquation[2] = m_lsPlaneVectors[2].z;

	//eventually we just have to compute the 'constant' coefficient a3
	//we use the fact that the plane pass through G --> GM.N = 0 (scalar prod)
	//i.e. a0*G[0]+a1*G[1]+a2*G[2]=a3
	m_lsPlaneEquation[3] = G.dot(m_lsPlaneVectors[2]);

	m_structuresValidity |= FLAG_LS_PLANE;

	return true;
}