Waymo Open Dataset

ailab·2022년 1월 26일
0
post-thumbnail

Waymo Open Dataset

Waymo Open Dataset에 대해 알아봅시다.

데이터 포맷과 불러오는 방식

이 데이터셋은 .tfrecord 파일들에 저장되어 있습니다. 우리는 먼저 모든 .tfrecord 파일 경로들의 리스트를 첫번째 파라미터로 갖는 TFRecordDataset을 생성해야 합니다.

train_set = tf.data.TFRecordDataset(train_files, compression_type='')

그 다음 다음과 같이train_set을 순회합니다. 해당 데이터셋의 각 샘플들은 각종 정보들이 포함된 프레임 입니다.

from waymo_open_dataset import dataset_pb2 as open_dataset
for i, data in enumerate(train_set):
    frame = open_dataset.Frame()
    frame.ParseFromString(bytearray(data.numpy()))

waymo_open_dataset은 사전 구성된 패키지입니다. for 반복문 안에서 framedata로부터 모든 정보를 로드합니다. 이 frame 내부에 어떤 것들이 내부를 살펴봅시다.

dataset 클래스 트리구조

  • 어떤 노드가 list of XXX를 가리키는 화살표가 표기되어 있다면 그 서브브랜치들은 해당 목록의 요소들입니다.
  • 대문자로 시작하는 노드들은 데이터셋의 소스코드에 포함된 클래스명을 의미합니다. 코드를 작성할 때는 그것들을 아래 예시와 같이 소문자로 변환해서 사용할 수 있습니다.
    frame.context.stats.location
    frame.camera_labels[0].labels[0].box.length
open_dataset
|-- LaserName
|   |-- UNKNOWN
|   |-- TOP
|   |-- FRONT
|   |-- SIDE_LEFT
|   |-- SIDE_RIGHT
|   `-- REAR
|-- CameraName
|   |-- UNKNOWN
|   |-- FRONT
|   |-- FRONT_LEFT
|   |-- FRONT_RIGHT
|   |-- SIDE_LEFT
|   `-- SIDE_RIGHT
|-- RollingShutterReadOutDirection
|   |-- UNKNOWN
|   |-- TOP_TO_BOTTOM
|   |-- LEFT_TO_RIGHT
|   |-- BOTTOM_TO_TOP
|   |-- RIGHT_TO_LEFT
|   `-- GLOBAL_SHUTTER
|-- Frame
|   |-- images ⇒ list of CameraImage
|   |   |-- name (CameraName)
|   |   |-- image
|   |   |-- pose
|   |   |-- velocity (v_x, v_y, v_z, w_x, w_y, w_z)
|   |   |-- pose_timestamp
|   |   |-- shutter
|   |   |-- camera_trigger_time
|   |   `-- camera_readout_done_time
|   |-- Context
|   |   |-- name
|   |   |-- camera_calibrations ⇒ list of CameraCalibration
|   |   |   |-- name
|   |   |   |-- intrinsic
|   |   |   |-- extrinsic
|   |   |   |-- width
|   |   |   |-- height
|   |   |   `-- rolling_shutter_direction (RollingShutterReadOutDirection)
|   |   |-- laser_calibrations ⇒ list of LaserCalibration
|   |   |   |-- name
|   |   |   |-- beam_inclinations
|   |   |   |-- beam_inclination_min
|   |   |   |-- beam_inclination_max
|   |   |   `-- extrinsic
|   |   `-- Stats
|   |       |-- laser_object_counts
|   |       |-- camera_object_counts
|   |       |-- time_of_day
|   |       |-- location
|   |       `-- weather
|   |-- timestamp_micros
|   |-- pose
|   |-- lasers ⇒ list of Laser
|   |   |-- name (LaserName)
|   |   |-- ri_return1 (RangeImage class)
|   |   |   |-- range_image_compressed
|   |   |   |-- camera_projection_compressed
|   |   |   |-- range_image_pose_compressed
|   |   |   `-- range_image
|   |   `-- ri_return2 (same as ri_return1)
|   |-- laser_labels ⇒ list of Label
|   |-- projected_lidar_labels (same as camera_labels)
|   |-- camera_labels ⇒ list of CameraLabels
|   |   |-- name (CameraName)
|   |   `-- labels ⇒ list of Label
|   `-- no_label_zones (Refer to the doc)
`-- Label
    |-- Box
    |   |-- center_x
    |   |-- center_y
    |   |-- center_z
    |   |-- length
    |   |-- width
    |   |-- height
    |   `-- heading
    |-- Metadata
    |   |-- speed_x
    |   |-- speed_y
    |   |-- accel_x
    |   `-- accel_y
    |-- type
    |-- id
    |-- detection_difficulty_level
    `-- tracking_difficulty_level

frame의 세부적인 내용들을 살펴봅시다.

frame 속성

LiDAR 데이터

Lidar 데이터는 frame.lasers을 통해 얻을 수 있습니다. 해당 객체는 위, 앞, 옆, 뒤 등의 각기 다른 위치의 라이다들을 가리키는 raw laser data들을 순회할 수 있습니다. raw laser data의 각 요소들은 각 라이다의 이름을 나타내는 .name 속성을 가집니다. 그 라이다 이름들은 open_dataset 패키지에 아래와 같은 상수로 정의되어 있습니다.

open_dataset.LaserName.UNKNOWN    = 0
open_dataset.LaserName.TOP        = 1
open_dataset.LaserName.FRONT      = 2
open_dataset.LaserName.SIDE_LEFT  = 3
open_dataset.LaserName.SIDE_RIGHT = 4
open_dataset.LaserName.REAR       = 5

포인트 클라우드 데이터에 접근해봅시다. 먼저 parse_range_image_and_camera_projection 함수를 사용하여 필요한 데이터들을 얻습니다.

(range_images, camera_projections, range_image_top_pose) = parse_range_image_and_camera_projection(frame)

range_images는 2차원 데이터입니다.

  • 첫번째 차원 : 라이다 이름 (LaserName)
  • 두번째 차원 : 신호 강도 (0: 강, 1:약)
  • 4개의 tuple로 이루어짐 (4채널 데이터)

위에서 획득한 세가지 데이터를 convert_range_image_to_point_cloud 함수를 통해 포인트 클라우드 데이터로 변환합니다.

points, cp_points = convert_range_image_to_point_cloud(frame,
                                                       range_images,
                                                       camera_projections,
                                                       range_image_top_pose)

이를 통해 range_images는 포인트 클라이드 데이터로 변환되어 points에 저장됩니다. 이는 길이 5인 리스트 데이터로, 3D 라이다 포인트 데이터 {[N, 3]}가 저장되어 있습니다.

len(points) return 5
points[2].shape returns (..., 3)

카메라 이미지

카메라 이미지는 frame.images를 통해 접근할 수 있스니다. 이 역시 라이다와 같이 앞, 옆, 뒤 등의 각기 다른 위치의 카메라를 가리키는 순회가능한 데이터로 구성되어 있습니다. 각 요소들은 카메라 이름을 가리키는 .name 속성을 가집니다. 카메라 이름은 open_dataset 패키지에 아래와 같이 정의되어 있습니다.

open_dataset.CameraName.UNKNOWN     = 0
open_dataset.CameraName.FRONT       = 1
open_dataset.CameraName.FRONT_LEFT  = 2
open_dataset.CameraName.FRONT_RIGHT = 3
open_dataset.CameraName.SIDE_LEFT   = 4
open_dataset.CameraName.SIDE_RIGHT  = 5

각 요소들은 .image 속성을 가지며 여기에 실제 이미지가 저장되어 있습니다. 카메라 이미지들은 JPEG 포맷입니다. 해당 이미지를 로컬 저장소에 저장하기 위한 코드는 다음과 같습니다.

from PIL import Image
import io
image = Image.open(io.BytesIO(frame.images[0].image))
image.save(filename, 'JPEG')

라이다 데이터의 포인트 클라우드 프로젝션

포인트 클라우드 프로젝션을 통해 이미지는 depth 정보와 결합될 수 있게 됩니다. 여기에는 프로젝션 라벨이 존재합니다.

바운딩 박스 라벨

2D labels

front_label = None
for lbl in frame.camera_labels:
    if lbl.name == open_dataset.CameraName.FRONT:
        front_label = lbl
        break

for label in front_label.labels:
    if label.type == 1:
        pass
  • 학습데이터의 첫 3배치, 검증데이터셋의 첫번째 배치에만 2D 라벨이 포함됩니다.
  • frame.camera_labels에는 각기 다른 5개의 카메라를 가리키는 5개의 요소들을 가질 수 있습니다. 각 lblCameraName과 같은 .name 속성을 가지고 이는 각각 하나의 특정 카메라와 연관되며, 특정 카메라로 촬영된 이미지에 포함된 모든 바운딩 박스 정보들이 담겨있습니다.
  • 위의 예제에서는 정면 카메라로 촬영한 이미지에 대한 라벨인 front_label을 얻고 있습니다. front_label.labels에는 하나의 이미지의 모든 바운딩박스 라벨이 저장되어 있습니다. .type 속성은 객체 타입(1=자동차)을 의미, .box 속성에는 바운딩박스 좌표가 저장되어 있습니다. 바운딩 박스에는 box = frame.camera_labels[0].labels[0].box과 같이 접근할 수 있습니다.
  • box의 속성과 의미는 다음과 같습니다.
    box.width : 상-하 길이
    box.length : 좌-우 길이
    box.center_x : length 방향의 중앙 x좌표
    box.center_y : width 방향의 중앙 y좌표

3D labels

for label in frame.laser_labels:
    if label.type == 1:
        pass

이는 카메라 라벨과 달리 어떤 라이다가 사용되었는지 구분하지 않고 해당 frame에서 모든 방향의 바운딩박스를 저장합니다.
label의 다른 속성들은 3D박스라는 것을 제외하고는 매우 단순합니다.
box.length <-> box.center_x (정면/앞 방향)
box.width <-> box.center_y (좌측 방향)
box.height <-> box.center_z (상/pointing-to-sky 방향)

참고

profile
인공지능과 관련된 지식들을 공유합니다.

0개의 댓글