PCL绘制自定义图形
在使用PCL拟合平面和柱面后,需要绘制自定义大小和位置的平面和柱面以便于可视化。
//拟合类型
enum FitType
{FitPlane = 1, //平面FitCylinder = 2 //圆柱
};
主要函数:
void PointCloudViewer(PointCloudT::Ptr cloudseg, PointCloudT::Ptr cloudfiltered, PointCloudT::Ptr cloudOut, pcl::ModelCoefficients::Ptr coefficients)
{pcl::visualization::PCLVisualizer viewer("viewer");float bckgr_gray_level = 0.0;float txt_gray_lvl = 1.0 - bckgr_gray_level;int v1(0);viewer.createViewPort(0.0, 0.0, 1.0, 1.0, v1);//分割点云pcl::visualization::PointCloudColorHandlerCustom<PointT> cloud_seg_color_h(cloudseg, 0, 255, 0);viewer.addPointCloud(cloudseg, cloud_seg_color_h, "cloudseg", v1);viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloudseg");//剩余点云pcl::visualization::PointCloudColorHandlerCustom<PointT> cloud_filter_color_h(cloudfiltered, 255, 255, 0);viewer.addPointCloud(cloudfiltered, cloud_filter_color_h, "cloudfilter", v1);viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloudfilter");//模型Eigen::Vector3f mass_center;if (mFitType == FitPlane){float width = 300.0, height = 300.0; //x,yGetPointsBoundingBoxAABB(cloudseg, width, height, mass_center);//分割平面:自定义的平面大小float scale[2] = { width, height };//利用PCL函数计算质心Eigen::Vector4f centroid; // 质心pcl::compute3DCentroid(*cloudseg, centroid); // 齐次坐标,(c0,c1,c2,1)auto plane = createPlane(*coefficients, centroid[0] - width / 2.0, centroid[1] - height / 2.0, centroid[2], scale);viewer.addModelFromPolyData(plane, "plane_1");viewer.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0.0, 1.0, 1.0, "plane_1", v1);viewer.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.6, "plane_1", v1);viewer.addText("point cloud plane", 10, 940, 16, bckgr_gray_level, txt_gray_lvl, txt_gray_lvl, "plane", v1);}else if (mFitType == FitCylinder){float width = 300.0, height = 300.0; //x,yGetPointsBoundingBoxAABB(cloudseg, width, height, mass_center);float scale = std::max(width / 2.0, height / 2.0);auto cylinder = createCylinder(*coefficients, mass_center, 100, scale);viewer.addModelFromPolyData(cylinder, "cylinder_1");viewer.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0.0, 1.0, 1.0, "cylinder_1", v1);viewer.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.6, "cylinder_1", v1);viewer.addText("point cloud cylinder", 10, 940, 16, bckgr_gray_level, txt_gray_lvl, txt_gray_lvl, "cylinder", v1);}//描述viewer.addText("point cloud seg", 10, 1000, 16, bckgr_gray_level, txt_gray_lvl, bckgr_gray_level, "cloudseg", v1);viewer.addText("point cloud filter", 10, 980, 16, txt_gray_lvl, txt_gray_lvl, bckgr_gray_level, "cloudfilter", v1);//背景 viewer.setBackgroundColor(bckgr_gray_level, bckgr_gray_level, bckgr_gray_level, v1);//相机位置if (mFitType == FitPlane)viewer.setCameraPosition(0.0, 0.0, coefficients->values[3] / 2.0, mass_center[0], mass_center[1], mass_center[2], 0.0, 1.0, 0.0);if (mFitType == FitCylinder)viewer.setCameraPosition(0.0, 0.0, coefficients->values[2] / 20.0, mass_center[0], mass_center[1], mass_center[2], 1.0, 0.0, 0.0);viewer.setSize(1280, 1024);//显示while (!viewer.wasStopped())viewer.spinOnce();
}
获取点云质心等参数:
void GetPointsBoundingBoxAABB(PointCloudT::Ptr cloudSource, float& width, float& height, Eigen::Vector3f& mass_center)
{// 创建惯性矩估算对象,设置输入点云,并进行计算pcl::MomentOfInertiaEstimation <pcl::PointXYZ> feature_extractor;feature_extractor.setInputCloud(cloudSource);feature_extractor.compute();std::vector <float> moment_of_inertia;std::vector <float> eccentricity;pcl::PointXYZ min_point_AABB;pcl::PointXYZ max_point_AABB;pcl::PointXYZ min_point_OBB;pcl::PointXYZ max_point_OBB;pcl::PointXYZ position_OBB;Eigen::Matrix3f rotational_matrix_OBB;float major_value, middle_value, minor_value;Eigen::Vector3f major_vector, middle_vector, minor_vector;// 获取惯性矩feature_extractor.getMomentOfInertia(moment_of_inertia);// 获取离心率feature_extractor.getEccentricity(eccentricity);// 获取AABB盒子feature_extractor.getAABB(min_point_AABB, max_point_AABB);// 获取OBB盒子feature_extractor.getOBB(min_point_OBB, max_point_OBB, position_OBB, rotational_matrix_OBB);feature_extractor.getEigenValues(major_value, middle_value, minor_value);// 获取主轴major_vector,中轴middle_vector,辅助轴minor_vectorfeature_extractor.getEigenVectors(major_vector, middle_vector, minor_vector);// 获取质心feature_extractor.getMassCenter(mass_center);//获取高度和宽度width = (max_point_AABB.x - min_point_AABB.x)*1.2;height = (max_point_AABB.y - min_point_AABB.y)*1.2;
}
创建一个自定义平面(源代码地址):
vtkSmartPointer<vtkPolyData> VisionTrajectory::createPlane(const pcl::ModelCoefficients &coefficients, double x, double y, double z, float scale[2])
{vtkSmartPointer<vtkPlaneSource> plane = vtkSmartPointer<vtkPlaneSource>::New();double norm_sqr = 1.0 / (coefficients.values[0] * coefficients.values[0] +coefficients.values[1] * coefficients.values[1] +coefficients.values[2] * coefficients.values[2]);plane->SetNormal(coefficients.values[0], coefficients.values[1], coefficients.values[2]);double t = x * coefficients.values[0] + y * coefficients.values[1] + z * coefficients.values[2] + coefficients.values[3];x -= coefficients.values[0] * t * norm_sqr;y -= coefficients.values[1] * t * norm_sqr;z -= coefficients.values[2] * t * norm_sqr;plane->SetCenter(x, y, z);{double pt1[3], pt2[3], orig[3], center[3];plane->GetPoint1(pt1);plane->GetPoint2(pt2);plane->GetOrigin(orig);plane->GetCenter(center);float scale1 = 3.0;float scale2 = 3.0;if (scale != nullptr){scale1 = scale[0];scale2 = scale[1];}//延长pt1,pt2. 延长origin到pt1连线的方向向量double _pt1[3], _pt2[3];for (int i = 0; i < 3; i++) {_pt1[i] = scale1 * (pt1[i] - orig[i]);_pt2[i] = scale2 * (pt2[i] - orig[i]);}//pt1相对于原坐标系下的坐标值for (int i = 0; i < 3; ++i){pt1[i] = orig[i] + _pt1[i];pt2[i] = orig[i] + _pt2[i];}plane->SetPoint1(pt1);plane->SetPoint2(pt2);}plane->Update();return (plane->GetOutput());
创建一个自定义柱面:
vtkSmartPointer<vtkPolyData> createCylinder(const pcl::ModelCoefficients &coefficients, Eigen::Vector3f mass_center, int numsides, float scale)
{if (coefficients.values.size() != 7){PCL_WARN("[addCylinder] Coefficients size does not match expected size (expected 7).\n");return (false);}//质心在轴线上的投影点P2double P0[3], P2[3];P0[0] = coefficients.values[0];P0[1] = coefficients.values[1];P0[2] = coefficients.values[2];double dlambda = (mass_center[0] - P0[0])*coefficients.values[3] + (mass_center[1] - P0[1])*coefficients.values[4] + (mass_center[2] - P0[2])*coefficients.values[5];P2[0] = P0[0] + dlambda * coefficients.values[3];P2[1] = P0[1] + dlambda * coefficients.values[4];P2[2] = P0[2] + dlambda * coefficients.values[5];//投影点平移double pt[3];pt[0] = coefficients.values[3] * scale;pt[1] = coefficients.values[4] * scale;pt[2] = coefficients.values[5] * scale;vtkSmartPointer<vtkLineSource> line = vtkSmartPointer<vtkLineSource>::New();line->SetPoint1(P2[0] - pt[0], P2[1] - pt[1], P2[2] - pt[2]);line->SetPoint2(P2[0] + pt[0], P2[1] + pt[1], P2[2] + pt[2]);vtkSmartPointer<vtkTubeFilter> tuber = vtkSmartPointer<vtkTubeFilter>::New();tuber->SetInputConnection(line->GetOutputPort());tuber->SetRadius(coefficients.values[6]);tuber->SetNumberOfSides(numsides);tuber->Update();return (tuber->GetOutput());
}