天天看點

處理點雲資料(一):點雲與生成鳥瞰圖

點雲資料

點雲資料一般表示為N行,至少三列的numpy數組。每行對應一個單獨的點,是以使用至少3個值的空間位置點(X, Y, Z)來表示。

處理點雲資料(一):點雲與生成鳥瞰圖

如果點雲資料來自于雷射雷達傳感器,那麼它可能有每個點的附加值,在KITTI資料中就有一個“反射率”,這是衡量雷射光束在那個位置被反射回來了多少。是以在KITTI資料中,其點雲資料就是N*4的矩陣。

圖像與點雲坐标

圖像的坐标軸和點雲的坐标軸是不同的,下圖顯示了藍色的圖像軸和橙色的點雲軸。

處理點雲資料(一):點雲與生成鳥瞰圖

關于圖像坐标軸:

1、圖像中的坐标值總是正的。

2、原點位于左上角。

3、坐标是整數值。

關于點雲坐标軸:

1、點雲的坐标值可以是正數也可以是負數。

2、坐标是實數。

3、正X軸代表前方。

4、正Y軸代表左邊。

5、正Z軸代表上方。

建立點雲資料中的鳥瞰圖

鳥瞰圖的相關坐标軸

處理點雲資料(一):點雲與生成鳥瞰圖

如上圖,鳥瞰圖中的點雲的相關軸為X和Y軸。同時,要考慮以下幾點:

1、X和Y軸意味着是相反的

2、X和Y軸指向相反方向的

3、将點雲的數值轉換到圖像上,使得(0, 0)是圖像中最小的值。

限制矩形區域

将注意力集中于點雲的特殊區域通常是有用的,對此,需要建立一個過濾器,保留感興趣區域内的點。

矩形區域設定為距離原點兩側10米,前方20米。

side_range=(-, ) # 最左到最右
fwd_range=(, ) # 最後到最前
           

建立一個過濾器,隻保留位于我們指定的矩形内的點

# 提取每個軸的點數
x_points = points[:, ]
y_points = points[:, ]
z_points = points[:, ]

# 過濾器 - 僅傳回立方體内點的索引
# 三個過濾器用于前後,左右,高度範圍
# 雷達坐标系中左側是正Y軸
f_filt = np.logical_and((x_points > fwd_range[]), (x_points < fwd_range[]))
s_filt = np.logical_and((y_points > -side_range[]), (y_points < -side_range[]))
filter = np.logical_and(f_filt, s_filt)
indices = np.argwhere(filter).flatten()

# 保留的點
x_points = x_points[indices]
y_points = y_points[indices]
z_points = z_points[indices]
           

映射點位置到像素位置

在點雲資料中,其資料為實數,而映射到的圖像資料為整數,若隻是單純的取整,将會丢失很多分辨率。

如果計量機關是米,想要一個5厘米的分辨率,則:

res =   # 轉換為像素位置的值 - 基于分辨率
x_img = (-y_points / res).astype(np.int32)  # X軸在點雲坐标系-Y上
y_img = (-x_points / res).astype(np.int32)  # Y軸在點雲坐标系-X上
           

轉換新的坐标原點

因為x, y 仍然有負值,是以需要移動資料使得(0, 0)為最小值。

x_img -= int(np.floor(side_range[] / res))
y_img += int(np.ceil(fwd_range[] / res))
           

像素值

将點雲資料(Z軸,代表高度)填充到圖像像素上去。設定一個想要關注的高度值範圍,高于或低于該範圍内的值都會被剪切到最大值和最小值,這能從感興趣區域獲得大量的細節。

根據點的高度填充到圖像上,調整在0-255之間。

height_range = (-, )  # 最低到最高

# 剪切高度值 
pixel_values = np.clip(a = z_points,
                       a_min=height_range[],
                       a_max=height_range[])
           

将值調整在0-255

def scale_to_255(a, min, max, dtype=np.uint8):
    """ 将指定的最小值,最大值範圍的值數組縮放到0-255
        可以指定輸出的資料類型(預設是uint8)
    """
    return (((a - min) / float(max - min)) * ).astype(dtype)

# 調整高度值
pixel_values  = scale_to_255(pixel_values, min=height_range[], max=height_range[])
           

建立圖像數組

# INITIALIZE EMPTY ARRAY - of the dimensions we want
x_max = +int((side_range[] - side_range[])/res)
y_max = +int((fwd_range[] - fwd_range[])/res)
im = np.zeros([y_max, x_max], dtype=np.uint8)

# FILL PIXEL VALUES IN IMAGE ARRAY
im[y_img, x_img] = pixel_values
           

可視化

from PIL import Image
im2 = Image.fromarray(im)
im2.show()
           

光譜色彩映射

import matplotlib.pyplot as plt
plt.imshow(im, cmap="spectral", vmin=, vmax=)
plt.show()
           

鳥瞰圖生成完整代碼

import numpy as np


# ==============================================================================
#                                                                   SCALE_TO_255
# ==============================================================================
def scale_to_255(a, min, max, dtype=np.uint8):
    """ Scales an array of values from specified min, max range to 0-255
        Optionally specify the data type of the output (default is uint8)
    """
    return (((a - min) / float(max - min)) * ).astype(dtype)


# ==============================================================================
#                                                         POINT_CLOUD_2_BIRDSEYE
# ==============================================================================
def point_cloud_2_birdseye(points,
                           res=,
                           side_range=(-, ),  # left-most to right-most
                           fwd_range = (-, ), # back-most to forward-most
                           height_range=(-, ),  # bottom-most to upper-most
                           ):
    """ Creates an 2D birds eye view representation of the point cloud data.

    Args:
        points:     (numpy array)
                    N rows of points data
                    Each point should be specified by at least 3 elements x,y,z
        res:        (float)
                    Desired resolution in metres to use. Each output pixel will
                    represent an square region res x res in size.
        side_range: (tuple of two floats)
                    (-left, right) in metres
                    left and right limits of rectangle to look at.
        fwd_range:  (tuple of two floats)
                    (-behind, front) in metres
                    back and front limits of rectangle to look at.
        height_range: (tuple of two floats)
                    (min, max) heights (in metres) relative to the origin.
                    All height values will be clipped to this min and max value,
                    such that anything below min will be truncated to min, and
                    the same for values above max.
    Returns:
        2D numpy array representing an image of the birds eye view.
    """
    # EXTRACT THE POINTS FOR EACH AXIS
    x_points = points[:, ]
    y_points = points[:, ]
    z_points = points[:, ]

    # FILTER - To return only indices of points within desired cube
    # Three filters for: Front-to-back, side-to-side, and height ranges
    # Note left side is positive y axis in LIDAR coordinates
    f_filt = np.logical_and((x_points > fwd_range[]), (x_points < fwd_range[]))
    s_filt = np.logical_and((y_points > -side_range[]), (y_points < -side_range[]))
    filter = np.logical_and(f_filt, s_filt)
    indices = np.argwhere(filter).flatten()

    # KEEPERS
    x_points = x_points[indices]
    y_points = y_points[indices]
    z_points = z_points[indices]

    # CONVERT TO PIXEL POSITION VALUES - Based on resolution
    x_img = (-y_points / res).astype(np.int32)  # x axis is -y in LIDAR
    y_img = (-x_points / res).astype(np.int32)  # y axis is -x in LIDAR

    # SHIFT PIXELS TO HAVE MINIMUM BE (0,0)
    # floor & ceil used to prevent anything being rounded to below 0 after shift
    x_img -= int(np.floor(side_range[] / res))
    y_img += int(np.ceil(fwd_range[] / res))

    # CLIP HEIGHT VALUES - to between min and max heights
    pixel_values = np.clip(a=z_points,
                           a_min=height_range[],
                           a_max=height_range[])

    # RESCALE THE HEIGHT VALUES - to be between the range 0-255
    pixel_values = scale_to_255(pixel_values,
                                min=height_range[],
                                max=height_range[])

    # INITIALIZE EMPTY ARRAY - of the dimensions we want
    x_max =  + int((side_range[] - side_range[]) / res)
    y_max =  + int((fwd_range[] - fwd_range[]) / res)
    im = np.zeros([y_max, x_max], dtype=np.uint8)

    # FILL PIXEL VALUES IN IMAGE ARRAY
    im[y_img, x_img] = pixel_values

    return im
           

特别說明:本文為本人學習所做筆記。

具體參考:http://ronny.rest/tutorials/module/pointclouds_01/point_cloud_birdseye/

繼續閱讀