(1) 3D ターゲット検出用の KITTI データセット

KITTI データセットの紹介

基本データ

KITTIこれは、ドイツのカールスルーエ工科大学とトヨタ シカゴ研究所のオープンソース データ セットで、年月号2012で初めて公開されました0320

対応する論文「自動運転の準備はできていますか? KITTI Vision Benchmark Suite」がに公開されましたCVPR2012

KITTIデータセットはドイツのカールスルーエから収集されており、都市/郊外/高速道路などの交通シーンが含まれています。年、月中に収集されます20110926/28/29/301003

KITTIデータ収集に使用されるプラットフォームを次の図に示します。

上記のプラットフォームには以下が含まれます

  • 140万画素白黒カメラ2台
  • 140万画素カラーカメラ2台
  • 4 エドモンド光学
  • 1 x 64 ライン Velodyne 3D レーザー スキャナ
  • 1 OXTS RT3003 慣性航法システム

上の図から分かるように

  • カメラの座標系は、Z軸が正面、Y軸が下を向き、座標系全体が右手座標系となる。
  • LIDAR の X 軸は真正面を向き、Z 軸は垂直上を向き、Y 軸は右手の法則に従って決定されます。
  • IMU/GPSシステムの座標系の方向は、LIDAR の座標系の方向と一致しています。

KITTIデータセットをまとめると、4カメラ、1ライダー、慣性航法1システムで構成されますIMU/GPSが、明らかにしたいのは6センサー。

センサーのサイズパラメータについては、以下の図を参照してください。

[date]-drive-sync-[sqquence]ディレクトリには6、各センサーに対応した収集データフォルダが格納されています

- image00
- image01
- image02
- image03
- oxts
- velodyne-points
  • Timestampvelodyne-pointsフォルダーの下に 3 つのタイムスタンプ ファイルがあります

    • timestamps_start.txt、レーザー スキャナが 1 週間でスキャンを開始する時間
    • timestamps_end.txt、レーザー スキャナによる 1 週間のスキャンが終了する時刻
    • timestamps.txtレーザーが前方に走査し、カメラが写真を撮るようになるまでの時間。
  • 画像データ

    • 画像はボンネットと空をトリミングした後のものです
    • 画像は歪み補正後のデータです
  • OSTX データ、各フレームには緯度と経度/速度/加速度を含む30異なるフィールド値が保存されます。

  • レーダーデータ

    • x,y,z浮動小数点数のバイナリ ファイル。各点は 4 つの浮動小数点数、レーダー座標系の座標、レーザーの反射強度で構成されます。r
    • スキャンごとに、およその120000ポイントが取得されます3D
    • LIDAR は垂直軸を中心に反時計回りに回転します

センサーの校正と時刻同期

システム全体はライダーの 1 回転を 1 フレームとして捉えます。ライダーが特定の位置まで回転すると、バネ板の物理的接触によってカメラがトリガーされて写真を撮影します。このトリガー方法ではデータを収集できませんが、頻度は高くなります。データ収集の時間が に達したIMU場合収集されたデータのタイムスタンプを記録し、カメラ収集時間に最も近いデータを現在のフレームのデータとして選択します。最大時間誤差は ですIMU100HZIMU5ms

カメラのキャリブレーション、カメラの内部および外部パラメータのキャリブレーション方法は、シングルショットを使用して距離センサーとカメラセンサーを自動キャリブレーションするためのツールボックス
です。すべてのカメラの中心は位置合わせされており、すべて同じXY平面上にあり、画像補正に便利です。 (スカイとボンネットを取り外します)

毎日のデータ収集を開始する前に、センサー間の位置ずれを避けるためにデータ収集システム全体が校正されます。

dailydate_calib.zipフォルダーの下に3つのテキストファイルがあります。

  • calib_cam_to_cam.txt
    外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします。
    最後の 3 つのパラメーター値の説明では、srect ( i ) s^{(i)}_{rect}s修正してください()補正(余分な空とフードを削除)後の画像のサイズを示します; R rect ( i ) \bold{R}^{(i)}_{rect}R修正してください()補正に使用される回転行列です; P rect ( i ) \bold{P}^{(i)}_{rect}P修正してください()はキャリブレーションに使用される射影行列です。i ∈ { 0 , 1 , 2 , 3 } i\in\{0,1,2,3\}{ 0 ,1 2 3 はカメラのシリアル番号を示し、0 は左グレースケール カメラ、1 は右グレースケール カメラ、2 は左カラー カメラ、3 は右カラー カメラを示します。左側のグレースケール カメラ 0 は参照カメラ座標系として使用され、3Dx = ( x , y , z , 1 ) \bold{x}=(x,y,z,1) となります。バツ=( x ,z 1 )番目のカメラ画像に変換するにはi、次の関係を使用できます:y = Prect ( i ) R rect ( 0 ) x \bold{y}=\bold{P}^{(i)}_{rect }\bold{R}^{(0)}_{rect}\bold{x}y=P修正してください()R修正してください( 0 )バツ

  • calib_velo_to_cam.txtKITTIデータセット LIDAR も、左側のグレースケール カメラ 0、レーザーからカメラまでの回転行列R velocam \bold{R}^{cam}_{velo} を基準にして校正されます。Rヴェロ_ _ _さあ_および翻訳ベクトルtvelocam \bold{t}^{cam}_{velo}tヴェロ_ _ _さあ_合成、同次変換行列は次のように書くことができます。
    T velocam = ( R velocamtvelocam 0 1 ) \bold{T}^{cam}_{velo}=\begin{pmatrix}R^{cam}_{velo} &t^{カム}_{ベロ} \\0 &1\end{pmatrix}Tヴェロ_ _ _さあ_=(Rヴェロ_ _ _さあ_0tヴェロ_ _ _さあ_1)
    このようにして、レーダー座標系の点をii3Di カメラ座標系の式は次のとおりです。 y = Prect ( i ) R rect ( 0 ) T velocamx \bold{y}=\bold{P}^{(i)}_{rect}\bold{R }^{ (0)}_{rect}\bold{T}^{cam}_{velo}\bold{x}y=P修正してください()R修正してください( 0 )Tヴェロ_ _ _さあ_バツ

  • calib_imu_to_velo.txt、このファイルは、imu同次変換行列T imuvelo \bold{T}_{imu}^{velo} を座標系からレーザー座標系に保存しますT私はあなたですヴェロ_ _ _したがって、IMU座標系の点を 2 番目の画像のピクセル座標3Dに変換する式はi、次のように書くことができます
    。 P }^{(i)}_{rect}\bold{R}^{(0)}_{rect}\bold{T}^{cam}_{velo}\bold{T}_{imu}^ { ベロ}\太字{x}y=P修正してください()R修正してください( 0 )Tヴェロ_ _ _さあ_T私はあなたですヴェロ_ _ _バツ

3D物体検出用

初期データセットによってサポートされるタスクは、両眼視、オプティカル フロー、オドメトリです。その後、深度推定、2D ターゲット検出、3D ターゲット検出、BEV ターゲット検出、セマンティック セグメンテーション、インスタンス セグメンテーション、マルチターゲット トラッキングなどのタスクが順次サポートされました。

ターゲット検出では、8 つのカテゴリが定義されています。

Car/Van/Truck/Pedestrian/Person_sitting/Cyclist/Tram/Misc(其他)

3D オブジェクトのラベル付けは LIDAR 座標系で実行されますが、 3D ターゲットの検出は、検出フレームの中心点の座標 xyz、検出フレームの長さ/幅/高さのみで構成されることに注意してくださいナビゲーション角度ヨーは 7 自由度で構成されます

外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします。

length/width/heightしかし、それはどの軸に相当するのか、曖昧になりやすい質問もありますxyzlength以下の図から、対応するdxwidth対応するdyheight対応することがわかりますdzボックス全体3Dが LIDAR 座標系でラベル付けされます。

KITTI 3D物体検出データセットには、7481Zhang トレーニング データと7518Zhang テスト データが含まれます。KITTIデータセットにはマークされた8オブジェクトが含まれていますが、アルゴリズムの評価に正式に使用されるCar/Pedestrianマークされたオブジェクトのみで十分ですBenchMark で使用される検出ボックスは 3 種類ありますKITTIKITTI3DCar/Pedestrian/Cyclist

ダウンロードした3Dターゲット検出データセットに含まれるフォルダーは次のとおりです。

  • image_02、左目用のpngカラー形式の画像
  • label_02、左目のカラー画像に注釈が付けられたオブジェクトラベル
  • calib、センサー間の座標変換関係
  • velodyne、レーザー点群データ
  • plane、レーザー座標系における路面の平面方程式

ラベル ファイルの各行の内容は次のとおりです。

Pedestrian 0.00 0 -0.20 712.40 143.00 810.73 307.92 1.89 0.48 1.20 1.84 1.47 8.41 0.01

含まれるフィールドは次のとおりです。

  • typeターゲットのタイプ ( Car/Van...、1 フィールド)
  • truncated浮動小数点数 0 ~ 1、カメラの視野から離れるターゲット オブジェクトの比率、1 フィールド
  • occluded整数0,1,2,30:すべて表示、1:部分的に遮蔽、2:ほとんど遮蔽されていない、3:不明、1 フィールド
  • alpha、物体の観察角度、1 フィールド[ − π , π ] [-\pi, \pi][ π ,p ]
  • bbox2D検出フレームのピクセル座標、x1,y1,x2,y2、4 フィールド
  • dimensions3Dオブジェクトのサイズ、height,width,length、単位は、 3 つのフィールド
  • location3Dカメラ座標系におけるオブジェクトの中心座標xyz、単位はm
  • rotation_yyaw角度、ヨー角、[ − π , π ] [-\pi, \pi][ π ,p ]
  • score、ターゲット オブジェクトのスコア。ROCカーブまたは を計算するために使用されますMAP

カメラ座標系の点は、calibの変換行列によって画像ピクセル座標に変換できます。

rotation_yalphaの違いはalpha、測定値がカメラの中心からオブジェクトの中心までの角度であることです。カメラ座標系の軸を中心としたオブジェクトの回転角度rotation_yを測定します車を例にとると、車がカメラ座標系の軸方向にある場合、車が平面内のどこにあっても角度はゼロになります。角度は、車がカメラ座標系の下軸上にある場合のみ角度がゼロとなり、軸から外れた場合は角度がゼロになりません。yyawxrotation_yx,zalphazalphazalpha

レーザー点群を左目のカラー画像に投影するために使用できる式は次のとおりですX=P2*R0_rect*Tr_vel_to_cam*Y

R0_rect3x3補正行列、Tr_vel_to_cam3x4カメラ座標系へのレーダー変換の変換行列です。

コードコンバット

を使用してopen3d点群を読み取ります

import numpy as np
import struct
import open3d as o3d

def convert_kitti_bin_to_pcd(binFilePath):
    size_float = 4
    list_pcd = []
    with open(binFilePath, "rb") as f:
        byte = f.read(size_float * 4)
        print(byte)
        while byte:
            x, y, z, intensity = struct.unpack("ffff", byte)
            list_pcd.append([x, y, z])
            byte = f.read(size_float * 4)
    np_pcd = np.asarray(list_pcd)
    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(np_pcd)
    return pcd

bs = "/xx/xx/data/code/mmdetection3d/demo/data/kitti/000008.bin"
pcds = convert_kitti_bin_to_pcd(bs)
o3d.visualization.draw_geometries([pcds])

# save
o3d.io.write_point_cloud('000008.pcd', pcds, write_ascii=False, compressed=False, print_progress=False)

読むことでnumpy

def load_bin(bin_file):
    data = np.fromfile(bin_file, np.float32).reshape((-1, 4))
    data = data[:, :-1]
    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(data)
    return pcd

上記のコードは、点群データを読み取って視覚化し、pcdフォーマットされた点群として保存できます。

選択した3Dターゲット検出タスク データセットtraining/image_02/000008.png

対応するタグファイル000008.txt

Car 0.88 3 -0.69 0.00 192.37 402.31 374.00 1.60 1.57 3.23 -2.70 1.74 3.68 -1.29
Car 0.00 1 2.04 334.85 178.94 624.50 372.04 1.57 1.50 3.68 -1.17 1.65 7.86 1.90
Car 0.34 3 -1.84 937.29 197.39 1241.00 374.00 1.39 1.44 3.08 3.81 1.64 6.15 -1.31
Car 0.00 1 -1.33 597.59 176.18 720.90 261.14 1.47 1.60 3.66 1.07 1.55 14.44 -1.25
Car 0.00 0 1.74 741.18 168.83 792.25 208.43 1.70 1.63 4.08 7.24 1.55 33.20 1.95
Car 0.00 0 -1.65 884.52 178.31 956.41 240.18 1.59 1.59 2.47 8.48 1.75 19.96 -1.25

上のように描くと000008.png

import cv2
img = cv2.imread(img_s)
with open(label_s) as f:
    lines = f.read().split("\n")[:-1]
for item in lines:
    boxes = item.split()[4:8]
    boxes = [float(x) for x in boxes]
    bb = np.array(boxes, dtype=np.int32)
    cv2.rectangle(img, bb[:2], bb[-2:], (0,0,255), 1)
cv2.imwrite("/xx/xx/data/code/mmdetection3d/demo/data/kitti/08_res.png", img)

左下隅にある 2 つの検出ボックスに注目してください。エッジが適切にマークされていません。

対応する点群データは次のとおりです。

KITTI立体物検出のデータラベルで与えられる中心点の座標は3D、左目カラーカメラ座標系です。

視覚検出フレームを使用するためのコードOpen3Dを参照できます。

"""
from https://github.com/dtczhl/dtc-KITTI-For-
Beginners.git
"""
import matplotlib.pyplot as plt
import matplotlib.image as mping
import numpy as np
import os
import open3d as o3d
import open3d.visualization as o3d_vis
from shapely.geometry import Point
from shapely.geometry.polygon import Polygon


MARKER_COLOR = {
    
    
    'Car': [1, 0, 0],               # red
    'DontCare': [0, 0, 0],          # black
    'Pedestrian': [0, 0, 1],        # blue
    'Van': [1, 1, 0],               # yellow
    'Cyclist': [1, 0, 1],           # magenta
    'Truck': [0, 1, 1],             # cyan
    'Misc': [0.5, 0, 0],            # maroon
    'Tram': [0, 0.5, 0],            # green
    'Person_sitting': [0, 0, 0.5]}  # navy

# image border width
BOX_BORDER_WIDTH = 5

# point size
POINT_SIZE = 0.005


def show_object_in_image(img_filename, label_filename):
    img = mping.imread(img_filename)
    with open(label_filename) as f_label:
        lines = f_label.readlines()
        for line in lines:
            line = line.strip('\n').split()
            left_pixel, top_pixel, right_pixel, bottom_pixel = [int(float(line[i])) for i in range(4, 8)]
            box_border_color = MARKER_COLOR[line[0]]
            for i in range(BOX_BORDER_WIDTH):
                img[top_pixel+i, left_pixel:right_pixel, :] = box_border_color
                img[bottom_pixel-i, left_pixel:right_pixel, :] = box_border_color
                img[top_pixel:bottom_pixel, left_pixel+i, :] = box_border_color
                img[top_pixel:bottom_pixel, right_pixel-i, :] = box_border_color
    plt.imshow(img)
    plt.show()

def show_object_in_point_cloud(point_cloud_filename, label_filename, calib_filename):
    pc_data = np.fromfile(point_cloud_filename, '<f4')  # little-endian float32
    pc_data = np.reshape(pc_data, (-1, 4))
    cloud = o3d.geometry.PointCloud()
    cloud.points = o3d.utility.Vector3dVector(pc_data[:,:-1])
    pc_color = np.ones((len(pc_data), 3))
    calib = load_kitti_calib(calib_filename)
    rot_axis = 2
    with open(label_filename) as f_label:
        lines = f_label.readlines()
        bboxes_3d = []
        for line in lines:
            line = line.strip('\n').split()
            point_color = MARKER_COLOR[line[0]]
            veloc, dims, rz, box3d_corner = camera_coordinate_to_point_cloud(line[8:15], calib['Tr_velo_to_cam'])
            bboxes_3d.append(np.concatenate((veloc, dims, np.array([rz]))))
        bboxes_3d = np.array(bboxes_3d)
        print(bboxes_3d.shape)
        lines = []
        for i in range(len(bboxes_3d)):
            center = bboxes_3d[i, 0:3]
            dim = bboxes_3d[i, 3:6]
            yaw = np.zeros(3)
            yaw[rot_axis] = bboxes_3d[i, 6]
            rot_mat = o3d.geometry.get_rotation_matrix_from_xyz(yaw)
            # bottom center to gravity center
            center[rot_axis] += dim[rot_axis] / 2

            box3d = o3d.geometry.OrientedBoundingBox(center, rot_mat, dim)
            
            line_set = o3d.geometry.LineSet.create_from_oriented_bounding_box(
                box3d)
            line_set.paint_uniform_color(np.array(point_color) / 255.)
            lines.append(line_set)
        
        for i, v in enumerate(pc_data):
            if point_in_cube(v[:3], box3d_corner) is True:
                pc_color[i, :] = point_color
                
        cloud.colors = o3d.utility.Vector3dVector(pc_color)
        o3d_vis.draw([*lines, cloud])

def point_in_cube(point, cube):
    z_min = np.amin(cube[:, 2], 0)
    z_max = np.amax(cube[:, 2], 0)

    if point[2] > z_max or point[2] < z_min:
        return False

    point = Point(point[:2])
    polygon = Polygon(cube[:4, :2])

    return polygon.contains(point)


def load_kitti_calib(calib_file):
    """
    This script is copied from https://github.com/AI-liu/Complex-YOLO
    """
    with open(calib_file) as f_calib:
        lines = f_calib.readlines()

    P0 = np.array(lines[0].strip('\n').split()[1:], dtype=np.float32)
    P1 = np.array(lines[1].strip('\n').split()[1:], dtype=np.float32)
    P2 = np.array(lines[2].strip('\n').split()[1:], dtype=np.float32)
    P3 = np.array(lines[3].strip('\n').split()[1:], dtype=np.float32)
    R0_rect = np.array(lines[4].strip('\n').split()[1:], dtype=np.float32)
    Tr_velo_to_cam = np.array(lines[5].strip('\n').split()[1:], dtype=np.float32)
    Tr_imu_to_velo = np.array(lines[6].strip('\n').split()[1:], dtype=np.float32)

    return {
    
    'P0': P0, 'P1': P1, 'P2': P2, 'P3': P3, 'R0_rect': R0_rect,
            'Tr_velo_to_cam': Tr_velo_to_cam.reshape(3, 4),
            'Tr_imu_to_velo': Tr_imu_to_velo}


def camera_coordinate_to_point_cloud(box3d, Tr):
    """
    This script is copied from https://github.com/AI-liu/Complex-YOLO
    """
    def project_cam2velo(cam, Tr):
        T = np.zeros([4, 4], dtype=np.float32)
        T[:3, :] = Tr
        T[3, 3] = 1
        T_inv = np.linalg.inv(T)
        lidar_loc_ = np.dot(T_inv, cam)
        lidar_loc = lidar_loc_[:3]
        return lidar_loc.reshape(1, 3)

    def ry_to_rz(ry):
        angle = -ry - np.pi / 2

        if angle >= np.pi:
            angle -= np.pi
        if angle < -np.pi:
            angle = 2 * np.pi + angle
        return angle

    h, w, l, tx, ty, tz, ry = [float(i) for i in box3d]
    cam = np.ones([4, 1])
    cam[0] = tx
    cam[1] = ty
    cam[2] = tz
    t_lidar = project_cam2velo(cam, Tr)

    Box = np.array([[-l / 2, -l / 2, l / 2, l / 2, -l / 2, -l / 2, l / 2, l / 2],
                    [w / 2, -w / 2, -w / 2, w / 2, w / 2, -w / 2, -w / 2, w / 2],
                    [0, 0, 0, 0, h, h, h, h]])

    rz = ry_to_rz(ry)

    rotMat = np.array([
        [np.cos(rz), -np.sin(rz), 0.0],
        [np.sin(rz), np.cos(rz), 0.0],
        [0.0, 0.0, 1.0]])

    velo_box = np.dot(rotMat, Box)

    cornerPosInVelo = velo_box + np.tile(t_lidar, (8, 1)).T

    box3d_corner = cornerPosInVelo.transpose()
    dims = np.array([l, w, h])
    # t_lidar: the x, y coordinator of the center of the object
    # box3d_corner: the 8 corners
    print(t_lidar.shape)
    return t_lidar.reshape(-1), dims, rz, box3d_corner.astype(np.float32)


if __name__ == '__main__':

    # updates
    ROOT = "/media/lx/data/code/mmdetection3d/demo/data/kitti"
    IMG_DIR = f'{
      
      ROOT}/image_2'
    LABEL_DIR = f'{
      
      ROOT}/label_2'
    POINT_CLOUD_DIR = f'{
      
      ROOT}/velo'
    CALIB_DIR = f'{
      
      ROOT}/calib'

    # id for viewing
    file_id = 8

    img_filename = os.path.join(IMG_DIR, '{0:06d}.png'.format(file_id))
    label_filename = os.path.join(LABEL_DIR, '{0:06d}.txt'.format(file_id))
    pc_filename = os.path.join(POINT_CLOUD_DIR, '{0:06d}.bin'.format(file_id))
    calib_filename = os.path.join(CALIB_DIR, '{0:06d}.txt'.format(file_id))

    # show object in image
    show_object_in_image(img_filename, label_filename)

    # show object in point cloud
    show_object_in_point_cloud(pc_filename, label_filename, calib_filename)

視覚化された結果は次のとおりです。

上記から、KITTIデータセットが3Dターゲット検出タスクに使用される状況について基本的に理解できます。後で、マルチモダリティを使用する場合に、2D検出フレームを組み合わせてターゲットを識別および位置特定する方法を追加します。


おすすめ

転載: blog.csdn.net/lx_ros/article/details/132592963