点云法线和曲率计算

点云法线估计原理参考:
[PCL]2 点云法向量计算NormalEstimation
点云法向量估计原理及应用PCL
点云曲率计算原理参考:
二次曲面拟合求点云的曲率
(曲率系列3:)PCL:PCL库中的两种曲率表示方法pcl::NormalEstimation和PrincipalCurvaturesEstimation
代码实现:

/**
 * @description:	最小二乘法拟合平面
 * @param cloud		输入点云
 * @return			平面方程系数
 */
std::vector<double> FitPlaneByLeastSquares(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud)
{
	if (cloud->points.size() < 3)	return {};

	double meanX = 0, meanY = 0, meanZ = 0;
	double meanXX = 0, meanYY = 0, meanZZ = 0;
	double meanXY = 0, meanXZ = 0, meanYZ = 0;
	for (int i = 0; i < cloud->points.size(); i++)
	{
		meanX += cloud->points[i].x;
		meanY += cloud->points[i].y;
		meanZ += cloud->points[i].z;

		meanXX += cloud->points[i].x * cloud->points[i].x;
		meanYY += cloud->points[i].y * cloud->points[i].y;
		meanZZ += cloud->points[i].z * cloud->points[i].z;

		meanXY += cloud->points[i].x * cloud->points[i].y;
		meanXZ += cloud->points[i].x * cloud->points[i].z;
		meanYZ += cloud->points[i].y * cloud->points[i].z;
	}
	meanX /= cloud->points.size();
	meanY /= cloud->points.size();
	meanZ /= cloud->points.size();
	meanXX /= cloud->points.size();
	meanYY /= cloud->points.size();
	meanZZ /= cloud->points.size();
	meanXY /= cloud->points.size();
	meanXZ /= cloud->points.size();
	meanYZ /= cloud->points.size();

	Eigen::Matrix3d m;
	m(0, 0) = meanXX - meanX * meanX; m(0, 1) = meanXY - meanX * meanY; m(0, 2) = meanXZ - meanX * meanZ;
	m(1, 0) = meanXY - meanX * meanY; m(1, 1) = meanYY - meanY * meanY; m(1, 2) = meanYZ - meanY * meanZ;
	m(2, 0) = meanXZ - meanX * meanZ; m(2, 1) = meanYZ - meanY * meanZ; m(2, 2) = meanZZ - meanZ * meanZ;
	Eigen::EigenSolver<Eigen::Matrix3d> PlMat(m * cloud->points.size());
	Eigen::Matrix3d eigenvalue = PlMat.pseudoEigenvalueMatrix();
	Eigen::Matrix3d eigenvector = PlMat.pseudoEigenvectors();

	double v1 = eigenvalue(0, 0), v2 = eigenvalue(1, 1), v3 = eigenvalue(2, 2);
	int minNumber = 0;
	if ((abs(v2) <= abs(v1)) && (abs(v2) <= abs(v3)))	minNumber = 1;
	if ((abs(v3) <= abs(v1)) && (abs(v3) <= abs(v2)))	minNumber = 2;
	double a = eigenvector(0, minNumber), b = eigenvector(1, minNumber), c = eigenvector(2, minNumber), d = -(a * meanX + b * meanY + c * meanZ);

	return{ a / sqrt(a*a + b*b + c*c), b / sqrt(a*a + b*b + c*c) , c / sqrt(a*a + b*b + c*c), std::min(v1,std::min(v2,v3)) / (v1 + v2 + v3) };
}

/**
 * @description:	最小二乘法拟合二次曲面
 * @param cloud		输入点云
 * @param point		给定点
 * @return			二次曲面给定点初的高斯曲率
 */
double FitQuadricByLeastSquares(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, pcl::PointXYZ& point)
{
	if (cloud->points.size() < 6)	return{};

	double a = 0, b = 0, c = 0, d = 0, e = 0, f = 0;	//二次曲面方程系数
	double u = 0, v = 0;								//二次曲面参数方程系数
	double E = 0, G = 0, F = 0, L = 0, M = 0, N = 0;	//二次曲面基本量

	double mean_curvature = 0, guass_curvature = 0;		//平均曲率和高斯曲率

	Eigen::Matrix<double, 6, 6> Q; //线性方程组系数矩阵
	Eigen::Matrix<double, 6, 1> B; //线性方程组右值矩阵

	Q.setZero();
	B.setZero();

	for (int i = 0; i < cloud->points.size(); i++)
	{
		Q(0, 0) += pow(cloud->points[i].x, 4);
		Q(1, 0) = Q(0, 1) += pow(cloud->points[i].x, 3)*cloud->points[i].y;
		Q(2, 0) = Q(0, 2) += pow(cloud->points[i].x*cloud->points[i].y, 2);
		Q(3, 0) = Q(0, 3) += pow(cloud->points[i].x, 3);
		Q(4, 0) = Q(0, 4) += pow(cloud->points[i].x, 2)*cloud->points[i].y;
		Q(5, 0) = Q(0, 5) += pow(cloud->points[i].x, 2);
		Q(1, 1) += pow(cloud->points[i].x*cloud->points[i].y, 2);
		Q(2, 1) = Q(1, 2) += pow(cloud->points[i].y, 3)*cloud->points[i].x;
		Q(3, 1) = Q(1, 3) += pow(cloud->points[i].x, 2)*cloud->points[i].y;
		Q(4, 1) = Q(1, 4) += pow(cloud->points[i].y, 2)*cloud->points[i].x;
		Q(5, 1) = Q(1, 5) += cloud->points[i].x*cloud->points[i].y;
		Q(2, 2) += pow(cloud->points[i].y, 4);
		Q(3, 2) = Q(2, 3) += pow(cloud->points[i].y, 2)*cloud->points[i].x;
		Q(4, 2) = Q(2, 4) += pow(cloud->points[i].y, 3);
		Q(5, 2) = Q(2, 5) += pow(cloud->points[i].y, 2);
		Q(3, 3) += pow(cloud->points[i].x, 2);
		Q(4, 3) = Q(3, 4) += cloud->points[i].x*cloud->points[i].y;
		Q(5, 3) = Q(3, 5) += cloud->points[i].x;
		Q(4, 4) += pow(cloud->points[i].y, 2);
		Q(5, 4) = Q(4, 5) += cloud->points[i].y;
		Q(5, 5) += 1;

		B(0, 0) += pow(cloud->points[i].x, 2)*cloud->points[i].z;
		B(1, 0) += cloud->points[i].x*cloud->points[i].y*cloud->points[i].z;
		B(2, 0) += pow(cloud->points[i].y, 2)*cloud->points[i].z;
		B(3, 0) += cloud->points[i].x*cloud->points[i].z;
		B(4, 0) += cloud->points[i].y*cloud->points[i].z;
		B(5, 0) += cloud->points[i].z;

		Eigen::Matrix<double, 6, 6> Q_inverse = Q.inverse();
		
		//求解二次曲面方程系数z=ax^2+bxy+cy^2+dx+ey+f
		for (size_t j = 0; j < 6; ++j)
		{
			a += Q_inverse(0, j)*B(j, 0);
			b += Q_inverse(1, j)*B(j, 0);
			c += Q_inverse(2, j)*B(j, 0);
			d += Q_inverse(3, j)*B(j, 0);
			e += Q_inverse(4, j)*B(j, 0);
			f += Q_inverse(5, j)*B(j, 0);
		}		
	}

	//求解二次曲面参数方程系数u=dz/dx,v=dz/dy
	u = 2 * a * point.x + b * point.y + d;
	v = 2 * c * point.y + b * point.x + e;

	//求解二次曲面第一基本量 E = u^2, F = uv, G = v^2
	E = 1 + u * u;
	F = u * v;
	G = 1 + v * v;

	//求解二次曲面第二基本量 L = p*n, M = r*n, N = q*n; p = d^2z/dx^2, q = d^2z/dy^2, r = d^2z/dxdy;n = (u×v) / |u×v|
	L = 2 * a / sqrt(1 + u * u + v * v);
	M = b / sqrt(1 + u * u + v * v);
	N = 2 * c / sqrt(1 + u * u + v * v);

	// 高斯曲率
	guass_curvature = (L * N - M * M) / (E * G - F * F);

	// 平均曲率
	mean_curvature = (E * N - 2 * F * M + G * L) / (2 * E * G - 2 * F * F);

	return guass_curvature;
}

/**
 * @description:	法线估计
 * @param cloud		输入点云
 * @param normals	法向量和曲率
 * @param nr_k		k邻近点数
 */
void normalestimation(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, pcl::PointCloud <pcl::Normal>::Ptr normals, int nr_k)
{
	pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
	kdtree.setInputCloud(cloud);
	std::vector<int> pointsIdx(nr_k);          
	std::vector<float> pointsDistance(nr_k);   

	for (size_t i = 0; i < cloud->size(); ++i)
	{
		kdtree.nearestKSearch(cloud->points[i], nr_k, pointsIdx, pointsDistance);    //k近邻搜索
		pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_temp(new pcl::PointCloud<pcl::PointXYZ>);
		pcl::copyPointCloud(*cloud, pointsIdx, *cloud_temp);
	
		std::vector<double> norm = FitPlaneByLeastSquares(cloud_temp);	//计算法向量
		//double curvature = FitQuadricByLeastSquares(cloud_temp, cloud->points[i]);		//计算曲率

		pcl::Normal p(norm[0], norm[1], norm[2]);
		p.curvature = norm[3];
		normals->push_back(p);
	}
}

代码传送门:https://github.com/taifyang/PCL-algorithm

你可能感兴趣的:(PCL,c++,PCL,点云处理)