求基于LAB色彩空间的PCL区域生长算法实现
基于LAB色彩空间的PCL区域生长算法实现方案
我刚好也做过类似的需求,分享一下完整的实现思路和代码示例,分为几个关键步骤:
1. 自定义带LAB色彩的点类型
PCL原生没有直接支持LAB的点结构体,所以我们先定义一个包含XYZ和LAB分量的自定义点类型:
#include <pcl/point_types.h> struct PointXYZLAB { PCL_ADD_POINT4D; // 内置XYZ坐标 float L, a, b; // LAB色彩分量 EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; // 注册自定义点类型,让PCL可以识别 POINT_CLOUD_REGISTER_POINT_STRUCT(PointXYZLAB, (float, x, x) (float, y, y) (float, z, z) (float, L, L) (float, a, a) (float, b, b))
2. 实现RGB到LAB的转换
PCL本身没有提供RGB转LAB的工具,我们可以借助OpenCV的色彩空间转换功能来完成(如果没装OpenCV,也可以手动实现RGB转CIE XYZ再转LAB的公式,但OpenCV更简便):
#include <opencv2/opencv.hpp> #include <pcl/point_cloud.h> #include <pcl/point_types.h> void rgbToLAB(const pcl::PointXYZRGB& rgb_point, PointXYZLAB& lab_point) { // 复制XYZ坐标 lab_point.x = rgb_point.x; lab_point.y = rgb_point.y; lab_point.z = rgb_point.z; // 将PCL的RGB格式(RGB顺序,0-255)转换为OpenCV的BGR格式 cv::Mat rgb_mat(1, 1, CV_8UC3); rgb_mat.at<cv::Vec3b>(0, 0)[0] = rgb_point.b; rgb_mat.at<cv::Vec3b>(0, 0)[1] = rgb_point.g; rgb_mat.at<cv::Vec3b>(0, 0)[2] = rgb_point.r; // RGB转LAB cv::Mat lab_mat; cv::cvtColor(rgb_mat, lab_mat, cv::COLOR_BGR2Lab); // 转换为浮点型并调整a、b分量的范围(从0-255转为-128到127) lab_point.L = static_cast<float>(lab_mat.at<cv::Vec3b>(0, 0)[0]); lab_point.a = static_cast<float>(lab_mat.at<cv::Vec3b>(0, 0)[1]) - 128.0f; lab_point.b = static_cast<float>(lab_mat.at<cv::Vec3b>(0, 0)[2]) - 128.0f; } // 批量转换整个点云 pcl::PointCloud<PointXYZLAB>::Ptr convertRGBCloudToLAB(const pcl::PointCloud<pcl::PointXYZRGB>::Ptr& rgb_cloud) { auto lab_cloud = std::make_shared<pcl::PointCloud<PointXYZLAB>>(); lab_cloud->resize(rgb_cloud->size()); for (size_t i = 0; i < rgb_cloud->size(); ++i) { rgbToLAB((*rgb_cloud)[i], (*lab_cloud)[i]); } return lab_cloud; }
3. 自定义LAB色彩空间的区域生长相似性判断
PCL的RegionGrowing类允许我们重写相似性判断逻辑,我们需要替换原有的RGB色彩相似度计算为LAB空间的距离计算(推荐用欧氏距离,或者更精准的CIEDE2000色差公式):
#include <pcl/segmentation/region_growing.h> #include <pcl/features/normal_3d.h> class RegionGrowingLAB : public pcl::RegionGrowing<PointXYZLAB, pcl::Normal> { protected: bool isSimilar(const PointXYZLAB& point_a, const PointXYZLAB& point_b, float distance) override { // 计算LAB色彩的欧氏距离 float delta_L = point_a.L - point_b.L; float delta_a = point_a.a - point_b.a; float delta_b = point_a.b - point_b.b; float color_distance = sqrt(delta_L*delta_L + delta_a*delta_a + delta_b*delta_b); // 如果需要更精准的色差,可以替换为CIEDE2000算法(需要额外实现) // float color_distance = calculateCIEDE2000(point_a, point_b); // 法线相似度判断(如果不需要法线约束,可以删除这部分) float normal_angle_threshold = this->getAngleThreshold(); if (pcl::getAngle3D(point_a.getNormalVector4fMap(), point_b.getNormalVector4fMap()) > normal_angle_threshold) { return false; } // 色彩相似度阈值(根据你的数据调整,一般10-20之间比较合适) return (color_distance < this->getColorThreshold()); } };
4. 完整的区域生长流程
把上面的部分整合起来,实现完整的LAB区域生长:
int main() { // 假设你已经有了输入的RGB点云 rgb_cloud pcl::PointCloud<pcl::PointXYZRGB>::Ptr rgb_cloud = ...; // 1. 转换为LAB点云 auto lab_cloud = convertRGBCloudToLAB(rgb_cloud); // 2. 计算法线(如果需要法线约束,不需要的话可以跳过这一步) pcl::NormalEstimation<PointXYZLAB, pcl::Normal> ne; pcl::PointCloud<pcl::Normal>::Ptr normals = std::make_shared<pcl::PointCloud<pcl::Normal>>(); auto tree = std::make_shared<pcl::search::KdTree<PointXYZLAB>>(); tree->setInputCloud(lab_cloud); ne.setInputCloud(lab_cloud); ne.setSearchMethod(tree); ne.setKSearch(20); // 调整邻域大小 ne.compute(*normals); // 3. 初始化并配置LAB区域生长 RegionGrowingLAB reg; reg.setInputCloud(lab_cloud); reg.setInputNormals(normals); // 如果不需要法线,注释掉这行 reg.setSearchMethod(tree); reg.setMinClusterSize(50); // 最小聚类大小 reg.setMaxClusterSize(100000); // 最大聚类大小 reg.setAngleThreshold(M_PI / 18.0); // 法线夹角阈值(10度,不需要的话可以设为0) reg.setColorThreshold(15.0); // LAB色彩距离阈值,根据数据调整 reg.setSmoothnessThreshold(0.1); // 平滑度阈值,可选 // 4. 提取聚类结果 std::vector<pcl::PointIndices> clusters; reg.extract(clusters); // 后续处理聚类结果... return 0; }
注意事项
- 阈值调整:LAB空间的色彩距离阈值需要根据你的数据集调整,一般10-20之间能得到较好的结果;法线阈值则根据点云的平滑程度设置。
- CIEDE2000色差:如果需要更贴合人眼感知的色差判断,可以实现CIEDE2000算法替换欧氏距离,不过计算量会有所增加。
- 无法线模式:如果不需要空间几何约束,可以使用
pcl::RegionGrowing<PointXYZLAB>(不带法线的版本),重写isSimilar时只判断色彩距离即可。
内容的提问来源于stack exchange,提问作者Tiago A. Silva
相关产品推荐
相关产品推荐

