python-pcl存储彩色点云至PCD中的rgb值发生变化导致图片失真

问题描述:python-pcl将彩色点云存储至PCD后,RGB的ASCII值发生巨大变化,导致图片失真。ASCII值如图1所示,原图片和失真图片如图2所示

img

图1 打开PCD的图,左为rosrun pcl_ros pcl_to_pcd出来的图,右为自己代码的图(RGB的部分不对),代码见下部代码块

img

图2 可视化点云图,左为正确图,右为失真图


import rospy
import numpy as np
import cv2
import pcl
from pcl import pcl_visualization
import ros_numpy
import open3d as o3d
from sensor_msgs.msg import PointCloud2
from sensor_msgs import point_cloud2
from sensor_msgs.msg import PointField

address = "/home/wmra/catkin_ws/src/my_pcl_tutorial/pictures2"

def morphological_primitives_running(msg):
    pcl_points = ros_numpy.numpify(msg) # ros_numpy.numpify()生成的图片是[Heijht,Weight,4],高宽和宽度是像素坐标,"4"是像素坐标上点云的xyz和颜色
    print("pcl_points",pcl_points)
    pcl_points_color = ros_numpy.point_cloud2.split_rgb_field(pcl_points) # 分离点云点的RGB通道
    print("pcl_points_color",pcl_points_color)
    height, width = pcl_points.shape[0], pcl_points.shape[1] # 图片像素高度,矩阵行数480 图片像素宽度,矩阵列数640 shape[2]是通道数 
    print("pcl_points.shape",pcl_points.shape)
    print("pcl_points_color['b']",pcl_points_color['b'])
    print("pcl_points_color['g']",pcl_points_color['g'])
    print("pcl_points_color['r']",pcl_points_color['r'])
    #img = cv2.imread("/home/wmra/catkin_ws/src/my_pcl_tutorial/scripts/grasp3_Color.png")
    #b,g,r = cv2.split(img)
    #img_rgb = cv2.merge([r,g,b])
    img_rgb = cv2.merge([np.array(pcl_points_color['b']), np.array(pcl_points_color['g']), np.array(pcl_points_color['r'])])
    #cv2.imshow("img_rgb", img_rgb)
    #cv2.waitKey(0)
    pcl_points_color_red, pcl_points_color_green, pcl_points_color_blue = np.resize(pcl_points_color['r'], height * width), np.resize(pcl_points_color['g'], height * width), np.resize(pcl_points_color['b'], height * width) # 将三色提取出来,[:, :, np.newaxis] np.newaxis放在哪个位置,就会给哪个位置增加维度
    pcl_points_color_red, pcl_points_color_green, pcl_points_color_blue = pcl_points_color_red.astype(np.uint32), pcl_points_color_green.astype(np.uint32), pcl_points_color_blue.astype(np.uint32)  # 
    pcl_pcd_points = np.zeros((height * width, 3), dtype=np.float32)
    pcl_pcd_rgb = np.zeros((height * width, 1), dtype=np.uint32)
    print("pcl_points_color['x']",pcl_points_color['x'])
    pcl_pcd_points[:, 0] = np.resize(pcl_points_color['x'], height * width) # 有序点云按照图片从左到右依次排列
    pcl_pcd_points[:, 1] = np.resize(pcl_points_color['y'], height * width)
    pcl_pcd_points[:, 2] = np.resize(pcl_points_color['z'], height * width)
    pcl_pcd_rgb[:, 0] = 255 << 24 | pcl_points_color_red << 16 | pcl_points_color_green << 8 | pcl_points_color_blue     # <<是左移运算符,转换为2进制,然后左移1位,此时十进制2变成十进制4
    pcl_pcd = np.concatenate((pcl_pcd_points, pcl_pcd_rgb), axis=1)
    print("pcl_pcd",pcl_pcd)
    cloud_points = pcl.PointCloud_PointXYZRGB()
    cloud_points.from_list(pcl_pcd)
    print(type(cloud_points))
    print(cloud_points[3])
    print("Saving points in 1.pcd")
    pcl.save(cloud_points, address + '/1.pcd', format = "pcd")  # 存储全部点云


def ros_running():
    rospy.init_node('morphological_primitives_ros', anonymous=True)
    rospy.Subscriber("/camera/depth_registered/points", PointCloud2, morphological_primitives_running, queue_size= 5)  #realsense
    rospy.spin()


if __name__ == "__main__":
   
    ros_running() 

补充描述

在pcl.save之前的 print("pcl_pcd",pcl_pcd) 结果为

pcl_pcd [[-2.90159917e+00 -2.20055056e+00  5.37000036e+00  4.28272238e+09]
 [-2.89273000e+00 -2.20055056e+00  5.37000036e+00  4.28298555e+09]
 [-2.86130571e+00 -2.18333936e+00  5.32800007e+00  4.28278842e+09]
 ...
 [            nan             nan             nan  4.28864552e+09]
 [            nan             nan             nan  4.28884213e+09]
 [            nan             nan             nan  4.28877633e+09]]

之后的print(cloud_points[3])结果为:


(-2.852505922317505, -2.1833393573760986, 5.328000068664551, 4282591232.0)

希望在不改变基本代码框架(用ros的数据即可)的情况下得到各位的指导

在将彩色点云存储至PCD文件中时,您将RGB信息合并到了一个32位的无符号整数中,但在存储之前,RGB通道的值需要进行类型转换,以适应这样的数据格式。在您的代码中,您使用了.astype(np.uint32)将RGB通道转换为32位的无符号整数,但这会导致RGB值的范围从0-255变为0-4294967295。在打开PCD文件时,一些软件将RGB值视为ASCII字符,因此在转换过程中发生了失真。为了解决这个问题,您可以将RGB值转换为0-255的范围,然后再将它们合并到一个32位的无符号整数中。例如,您可以使用以下代码转换RGB通道:

[pcl_points_color_red = (pcl_points_color['r'] * 255).astype(np.uint32)
pcl_points_color_green = (pcl_points_color['g'] * 255).astype(np.uint32)
pcl_points_color_blue = (pcl_points_color['b'] * 255).astype(np.uint32)]()

这将RGB值从0-1的范围转换为0-255的范围,以便在存储到PCD文件中时保留颜色信息。

在使用 Python-PCL 存储彩色点云至 PCD 文件时,可能会出现 RGB 值变化导致图片失真的情况。这可能是因为在转换 RGB 值时,将 RGB 值从 uint8_t 类型转换为 float 类型,然后在将其存储到 PCD 文件时,将其再次转换为 uint8_t 类型,从而导致精度损失。

可用以下方法避免这种情况:

1.在将 RGB 值从 uint8_t 转换为 float 时,可以将其除以 255.0,这样可以将其归一化为 0 到 1 的浮点数。

2.在将 RGB 值从 float 转换为 uint8_t 时,可以使用 round 函数来四舍五入到最接近的整数。

3.如果需要在不同软件之间共享点云数据,可以考虑使用更通用的文件格式,如PLY格式,它支持多种数据类型和精度,以及支持点云颜色的存储。

问题描述中提到将彩色点云存储至PCD后,RGB的ASCII值发生巨大变化,导致图片失真。这可能是由于彩色点云的RGB通道未被正确地解析所导致的。
1.解答方案:

为解决此问题,可以检查点云RGB通道的数据类型是否正确,应为uint8或uint32,应确保所有颜色值都在0到255之间。在存储PCD文件之前,应将RGB通道重新合并,并使用uint32数据类型表示RGB颜色。保存点云时,使用PCD_V7格式,因为它支持uint32类型。最后,应使用PointCloud_PointXYZRGB类来加载PCD文件,因为它支持颜色数据。

2.代码修改:
(1)将以下代码:

pcl_points_color_red, pcl_points_color_green, pcl_points_color_blue = pcl_points_color_red.astype(np.uint32), pcl_points_color_green.astype(np.uint32), pcl_points_color_blue.astype(np.uint32)

修改为:

pcl_points_color_red, pcl_points_color_green, pcl_points_color_blue = pcl_points_color_red.astype(np.uint8), pcl_points_color_green.astype(np.uint8), pcl_points_color_blue.astype(np.uint8)

(2)将以下代码:

pcl_pcd_rgb[:, 0] = 255 << 24 | pcl_points_color_red << 16 | pcl_points_color_green << 8 | pcl_points_color_blue

修改为:

pcl_pcd_rgb[:, 0] = pcl_points_color_red.astype(np.uint32) << 16 | pcl_points_color_green.astype(np.uint32) << 8 | pcl_points_color_blue.astype(np.uint32)

你使用了以下语句将RGB值转换为ASCII码值:

pcl_pcd_rgb[:, 0] = 255 << 24 | pcl_points_color_red << 16 | pcl_points_color_green << 8 | pcl_points_color_blue

这种转换方式可能会出现数据截断的情况,导致RGB值的失真。建议使用以下方式进行RGB值的转换:

pcl_pcd_rgb = np.zeros((height * width, 1), dtype=np.uint8)
pcl_pcd_rgb[:, 0] = np.concatenate([pcl_points_color_red[:, np.newaxis], pcl_points_color_green[:, np.newaxis], pcl_points_color_blue[:, np.newaxis]], axis=1)

这种转换方式将RGB三个通道的值直接合并在一个数组中,避免了数据截断的问题,同时也可以保证RGB值的顺序正确。可以尝试修改代码,看看是否能够解决RGB值失真的问题。

不过根据你的补充描述,代码似乎没有问题。可以尝试使用其他的PCD文件查看是否存在类似的RGB值失真问题,以确定问题是否出现在文件编码或文件格式上。同时,可以查看存储的PCD文件是否正确读入,以确保文件内容正确。另外,您也可以尝试使用其他的点云库或工具来读取和显示PCD文件,以检查RGB值是否正确

在使用python-pcl存储彩色点云时,可能需要注意一些点。因为PCD文件默认使用ASCII编码,而ASCII编码的范围是0-127,而颜色通道的值域为0-255。如果不将颜色值转换为0-127的范围内的值,可能会出现失真的情况。以下是一个可能的解决方案:

import pcl

# 读取彩色点云
cloud = pcl.load('colored_cloud.pcd')

# 将RGB值除以4,转换到0-127范围内
cloudrgb = cloud.to_array()
cloudrgb[:, :, 3:] = cloudrgb[:, :, 3:] / 4
cloud = pcl.PointCloud_PointXYZRGB()
cloud.from_array(cloudrgb)

# 存储点云
pcl.save(cloud, 'colored_cloud_new.pcd', format='pcd')


在上述代码中,我们首先读取彩色点云文件colored_cloud.pcd。然后,将点云转换为一个二维数组,其中第3列到第5列为RGB颜色通道的值。我们将这些值除以4,将其范围转换为0-127,然后重新构造点云对象,并使用新的颜色通道保存点云。最后,我们将点云保存为colored_cloud_new.pcd文件。

为了解决这个问题,可以在生成图片之前将RGB值转换回0-255的范围。可以使用以下公式进行转换:

```R = (int(r) >> 16) & 0x0000ff
G = (int(g) >> 8) & 0x0000ff
B = int(b) & 0x0000ff
其中,r、g和b是从PCD文件读取的RGB整数值。转换后,可以将R、G和B值用于生成图像。

另外,如果PCL库中使用的点云数据类型是PointXYZRGB,可以使用pcl::PointCloudpcl::PointXYZRGB::Ptr读取和写入彩色点云数据,并使用pcl::io::savePCDFileASCII()函数将点云保存为ASCII格式的PCD文件。可以在读取点云数据时使用pcl::io::loadPCDFile()函数,将点云数据读取为pcl::PointCloudpcl::PointXYZRGB::Ptr类型,以便进行后续处理。