|
|
1. waymo dataset 시각화
구글에서 제공하는 waymo dataset을 open3d 라이브러리를 이용하여 시각화해보겠음
2. waymo dataset 소개
perception dataset -> high resolution sensor(lidar, camera) data and annotations (labels) for 1,950 segments 20s each
motion dataset -> object trajectories and corresponding 3D maps for 103,354 segments 20s each
센서 종류
- Lidar sensor 5개(top, front, rear, left, right)
- high-resolution camera 5대(front, front left, front right, side left, side right)
측정방법
모든 센서의 측정 주기 : 10Hz(100msec)
20초단위로 연속된 장면을 측정한것을 segment라고 부름
1개의 세그먼트는 20secx10Hz=200개의 프레임(영상 또는 포인트 클라우드)으로 구성됨
Lidar 측정 데이터
range : 라이다좌표계원점에서 포인트까지 거리
intensity : 레이저 반사파의 강도
elongation : spurious objects(dust, fog, rain) 측정을 위한 추가된 intensity
no label zone : no label zone에 포함여부
vehicle pose : 측정시 차량의 자세정보
camera projections : 라이다 포인트를 영상에 투사한 점
ground truth annotations(label 정보)
LiDAR sensor readings -> 3D bounding boxes
the camera images -> 2D bounding boxes
4 object categories: vehicles, pedestrians, cyclists, signs
waymo dataset을 설명하는 공식논문
파일포맷에 대한 자세한 설명은 아래의 사이트를 참고하기 바람
https://github.com/waymo-research/waymo-open-dataset
https://tkayyoo.tistory.com/14
multiple return of lidar
https://home.iitk.ac.in/~blohani/LiDAR_Tutorial/Multiple%20return%20LiDAR.htm
3. waymo dataset 다운로드
https://waymo.com/open/ 에 접속하여 사용자 정보 입력 후 로그인
로그인 후 아래와 같은 데이터셋에서 3d object detection은 Perception Dataset을 다운받으면 되는데 v1.4.3은 tfrecord 형식이고 v2.0.1은 parquet 형식임, 여기서는 tfrecord형식의 v1.4.3을 이용하겠음
다운로드 버튼을 클릭하면 아래와 같은 화면이 나오는데 여러개의 파일을 동시에 다운로드하려면 gcloud cli 도구를 이용해야 함
여기서는 테스트를 위한 training 폴더에 있는 tfrecord 파일 5개를 수동으로 1개씩 다운로드 하겠음 파일의 우측끝에 있는 다운로드 버튼를 누르면 1개씩 다운로드 가능함 1개당 약 1GB 용
다운로드한 파일 목록은 다음과 같다.
각각의 파일은 1개의 segment에 해당하는 데이터임 -> 즉 20초동안에 측정된 200프레임의 포인트 클라우드, 영상, 레이블 등이 포함됨
(중요) 윈도우즈에서 다운받아 저장하고 wsl2-ubuntu24.04에서 폴더를 공유할 수 있다. -> 리눅스 디렉토리로 복사할 필요없음
윈도우즈 C드라이브 -> /mnt/c
윈도우즈 d드라이브 -> /mnt/d
4. 개발환경 구축
waymo dataset 관련 패키지가 현재 리눅스용만 지원함
GPU 없어도 실행됨
개발환경 : wsl2-ubuntu24.04, anaconda 가상환경, python3.10
(1) anaconda 설치
wsl2-ubuntu24.04에 설치는 아래사이트의 linux installer -> linux x86 설치법을 따라하면 됨
https://www.anaconda.com/docs/getting-started/anaconda/install#macos-linux-installation
설치완료 후 리눅스 종료 후 다시 실행하면 아래처럼 가상환경(base)이 활성화됨
(2) anaconda 가상환경 생성
$ conda create --name=waymo python=3.10
$ pip install pip --upgrade
(3) 의존패키지 설치
$ conda activate waymo -> 가상환경 waymo 활성화후 설치진행
$ pip install open3d==0.16.0 # pillow 9.2.0과 호환되는 버전으로 설치함
$ pip install opencv-python
$ pip install waymo-open-dataset-tf-2-11-0==1.6.1 -> 가장 중요한 패키지이므로 이것을 기준으로 다른 패키지의 버전을 맞출것
$ sudo apt install libgomp1 -> 리눅스 패키지
5. 포인트 클라우드 시각화
open3d을 이용하여 waymo point cloud를 시각화해보고자 함
아래의 사이트의 내용을 참고하여 작성했음
https://salzi.blog/2022/05/14/waymo-open-dataset-open3d-point-cloud-viewer/
https://github.com/KushalBKusram/WaymoOpenDatasetToolKit
tfrecord 파일에 약 200개 프레임이 저장되어 있는데 이중에서 1개만 추출해서 시각화해보고자 함
총 5개의 라이다 센서에서 측정한 데이터를 모두 출력해줌
라이다센서는 1개당 2개의 range image(first return, second return)로 구성됨
<소스코드>
import os
import numpy as np
import tensorflow as tf
import open3d as o3d
from waymo_open_dataset.dataset_pb2 import Frame
from waymo_open_dataset.utils import frame_utils
segment_path = r"/mnt/d/Users/2sungryul/Dropbox/Work/Dataset/Waymo/individual_files_training_segment-10017090168044687777_6380_000_6400_000_with_camera_labels.tfrecord"
# 데이터셋파일의 확장자가 tfrecord인지 체크 후 에러처리
if os.path.basename(segment_path).split('.')[-1] != 'tfrecord':
raise ValueError(f'segment file has to be of ' f'{tf.data.TFRecordDataset.__name__} type')
# tfrecord파일로부터 tf.data.TFRecordDataset 객체생성(약 200개 포인트 클라우드 프레임으로 구성됨)
data_set = tf.data.TFRecordDataset(segment_path, compression_type='')
# tf.data.TFRecordDataset 객체로부터 프레임(tf.Tensor) 1개를 추출
frame_index = 100
for data in data_set.skip(frame_index).take(1):
break
# point cloud frame 객체생성
frame = Frame()
frame.ParseFromString(bytearray(data.numpy()))
range_images, camera_projections, _, range_image_top_pose = frame_utils.parse_range_image_and_camera_projection(frame)
frame.lasers.sort(key=lambda laser: laser.name)
# first return of lidar
points0, points_cp0 = frame_utils.convert_range_image_to_point_cloud(frame, range_images, camera_projections, range_image_top_pose, ri_index=0)
# second return of lidar
points1, points_cp1 = frame_utils.convert_range_image_to_point_cloud(frame, range_images, camera_projections, range_image_top_pose, ri_index=1)
print('frame index:', frame_index)
print('first return:',len(points0), len(points0[0]),len(points0[1]),len(points0[2]),len(points0[3]),len(points0[4]))
print('total point:',len(points0[0])+len(points0[1])+len(points0[2])+len(points0[3])+len(points0[4]))
print('second return:',len(points1), len(points1[0]),len(points1[1]),len(points1[2]),len(points1[3]),len(points1[4]))
print('total point:',len(points1[0])+len(points1[1])+len(points1[2])+len(points1[3])+len(points1[4]))
# merge first and second returns
points_concat = np.concatenate(points0 + points1, axis=0)
#points_concat = np.concatenate(points0, axis=0)
print(f'points_concat shape: {points_concat.shape}')
# visualize point cloud
vis = o3d.visualization.VisualizerWithKeyCallback()
vis.create_window()
opt = vis.get_render_option()
opt.background_color = np.asarray([0, 0, 0])
opt.point_size = 1.0
mesh_frame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=1.6, origin=[0, 0, 2])
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points_concat)
pcd.colors = o3d.utility.Vector3dVector(np.ones((points_concat.shape[0], 3)))
vis.add_geometry(pcd)
vis.add_geometry(mesh_frame)
vis.run()
<실행결과>
위 에러메시지는 아래 환경변수를 정의해주면 에러메시지 해결됨
기타 에러는 인터넷 검색하여 해결할것
$ export GALLIUM_DRIVER=d3d12
$ export LIBGL_ALWAYS_INDIRECT=0
$ export MESA_GL_VERSION_OVERRIDE=4.5
$ export MESA_GLSL_VERSION_OVERRIDE=450
$ export LIBGL_ALWAYS_SOFTWARE=1
https://github.com/isl-org/Open3D/issues/4429
라이다 5대, 각각의 포인트 갯수를 출력, first return은 라이다의 첫번째 반사파를 의미하고 second return은 두번째 반사파를 의미함, 모든 측정데이터를 합쳐서 출력함, 총개수가 183,680개이고 (x,y,z) 3차원 데이터임을 의미함
frame_index = 0
frame_index = 50
6. 영상 시각화
프레임에서 영상(5대 카메라)을 추출하여 영상파일로 디스크에 저장하고 화면에 출력
<소스코드>
# visualize 5 images extracted from waymo_open_dataset
import os
import tensorflow as tf
import cv2
import matplotlib.pyplot as plt
from waymo_open_dataset.dataset_pb2 import Frame
CAMERA_NAME = {
0: 'unknown',
1: 'front',
2: 'front-left',
3: 'front-right',
4: 'side-left',
5: 'side-right'
}
segment_path = r"/mnt/d/Users/2sungryul/Dropbox/Work/Dataset/Waymo/individual_files_training_segment-10017090168044687777_6380_000_6400_000_with_camera_labels.tfrecord"
# 데이터셋파일의 확장자가 tfrecord인지 체크 후 에러처리
if os.path.basename(segment_path).split('.')[-1] != 'tfrecord':
raise ValueError(f'segment file has to be of ' f'{tf.data.TFRecordDataset.__name__} type')
# tfrecord파일로부터 tf.data.TFRecordDataset 객체생성(약 200개 포인트 클라우드 프레임으로 구성됨)
data_set = tf.data.TFRecordDataset(segment_path, compression_type='')
# tf.data.TFRecordDataset 객체로부터 프레임(tf.Tensor) 1개를 추출
frame_index = 0
for data in data_set.skip(frame_index).take(1):
break
# frame 객체생성
frame = Frame()
# tfrecord 객체데이터를 파싱
frame.ParseFromString(bytearray(data.numpy()))
# 카메라 5대의 영상을 이름으로 정렬
images = sorted(frame.images, key=lambda i: i.name)
for i, camera_image in enumerate(images):
image = tf.image.decode_png(camera_image.image)
image = cv2.cvtColor(image.numpy(), cv2.COLOR_RGB2BGR)
print(i,CAMERA_NAME[camera_image.name],image.shape)
# frame_index 번째 프레임의 5대 카메라 영상을 파일로 저장
cv2.imwrite("frame{}-{}.png".format(frame_index,CAMERA_NAME[camera_image.name]), image)
image = cv2.imread("frame{}-{}.png".format(frame_index,CAMERA_NAME[camera_image.name]))
#plt.imshow(image)
cv2.imshow("image{}".format(i),image)
cv2.waitKey()
<실행결과>
5대의 카메라 이름과 영상사이즈 출력, 정면3대와 측면 2대의 해상도가 약간다름 (높이, 폭, 채널)
5개의 영상을 디스크에 저장
5개 영상을 화면에 출력-> 정면 카메라 영상만 첨부했음, 라이다 포인트 클라우드와 비교해볼 것
7. 포인트 클라우드에 레이블 3d 바운딩 박스 그리기
1개 프레임의 포인트 클라우드를 그리고 레이블정보에 포함된 3차원 바운딩박스정보를 추출하여 그려준다.
레이블 3차원 바운딩 박스는 5개의 카메라를 이용하여 레이블링한 것으로 추측됨 -> 차량의 주위에 모든 방향에 car, pedestrian, sign, cyclist 4가지 클래스를 표시해줌
waymo_utils은 아래 깃허브에서 다운받을것
https://github.com/2sungryul/waymo_visualization
<소스코드>
# visualize point cloud and 3d gtbbox
import os
import numpy as np
import tensorflow as tf
import open3d as o3d
from waymo_open_dataset.dataset_pb2 import Frame
from waymo_open_dataset.utils import frame_utils
import waymo_utils
segment_path = r"/mnt/d/Users/2sungryul/Dropbox/Work/Dataset/Waymo/individual_files_training_segment-10017090168044687777_6380_000_6400_000_with_camera_labels.tfrecord"
# 데이터셋파일의 확장자가 tfrecord인지 체크 후 에러처리
if os.path.basename(segment_path).split('.')[-1] != 'tfrecord':
raise ValueError(f'segment file has to be of ' f'{tf.data.TFRecordDataset.__name__} type')
# tfrecord파일로부터 tf.data.TFRecordDataset 객체생성(약 200개 포인트 클라우드 프레임으로 구성됨)
data_set = tf.data.TFRecordDataset(segment_path, compression_type='')
# tf.data.TFRecordDataset 객체로부터 프레임(tf.Tensor) 1개를 추출
frame_index = 0
for data in data_set.skip(frame_index).take(1):
break
# point cloud frame 객체생성
frame = Frame()
frame.ParseFromString(bytearray(data.numpy()))
range_images, camera_projections, _, range_image_top_pose = frame_utils.parse_range_image_and_camera_projection(frame)
frame.lasers.sort(key=lambda laser: laser.name)
# first return of lidar
points0, points_cp0 = frame_utils.convert_range_image_to_point_cloud(frame, range_images, camera_projections, range_image_top_pose, ri_index=0)
# second return of lidar
points1, points_cp1 = frame_utils.convert_range_image_to_point_cloud(frame, range_images, camera_projections, range_image_top_pose, ri_index=1)
print('frame index:', frame_index)
print('first return:',len(points0), len(points0[0]),len(points0[1]),len(points0[2]),len(points0[3]),len(points0[4]))
print('total point:',len(points0[0])+len(points0[1])+len(points0[2])+len(points0[3])+len(points0[4]))
print('second return:',len(points1), len(points1[0]),len(points1[1]),len(points1[2]),len(points1[3]),len(points1[4]))
print('total point:',len(points1[0])+len(points1[1])+len(points1[2])+len(points1[3])+len(points1[4]))
# merge first and second returns
points_concat = np.concatenate(points0 + points1, axis=0)
#points_concat = np.concatenate(points0, axis=0)
print(f'points_concat shape: {points_concat.shape}')
waymo_utils.show_point_cloud_binary(points_concat, frame.laser_labels)
#waymo_utils.show_point_cloud_rainbow(points_concat, frame.laser_labels)
<실행결과>
frame_index = 0에 해당하는 프레임을 시각화 한것임 다른 프레임을 시각화하려면 변수값을 변경해주면 됨(0~약200)
frame_index =100에 해당하는 프레임을 시각화 한것임
8. 영상위에 2차원 바운딩 박스 그리기
정면카메라에서 측정된 영상위에 2차원 바운딩 박스를 그려준다.
waymo_utils은 아래 깃허브에서 다운받을것
https://github.com/2sungryul/waymo_visualization
<소스코드>
# visualize 2d bbox on front image
import os
import tensorflow as tf
import cv2
import matplotlib.pyplot as plt
from waymo_open_dataset.dataset_pb2 import Frame
import waymo_utils
CAMERA_NAME = {
0: 'unknown',
1: 'front',
2: 'front-left',
3: 'front-right',
4: 'side-left',
5: 'side-right'
}
segment_path = r"/mnt/d/Users/2sungryul/Dropbox/Work/Dataset/Waymo/individual_files_training_segment-10017090168044687777_6380_000_6400_000_with_camera_labels.tfrecord"
# 데이터셋파일의 확장자가 tfrecord인지 체크 후 에러처리
if os.path.basename(segment_path).split('.')[-1] != 'tfrecord':
raise ValueError(f'segment file has to be of ' f'{tf.data.TFRecordDataset.__name__} type')
# tfrecord파일로부터 tf.data.TFRecordDataset 객체생성(약 200개 포인트 클라우드 프레임으로 구성됨)
data_set = tf.data.TFRecordDataset(segment_path, compression_type='')
# tf.data.TFRecordDataset 객체로부터 프레임(tf.Tensor) 1개를 추출
frame_index = 100
for data in data_set.skip(frame_index).take(1):
break
# frame 객체생성
frame = Frame()
# tfrecord 객체데이터를 파싱
frame.ParseFromString(bytearray(data.numpy()))
# 카메라 5대의 영상을 이름으로 정렬
images = sorted(frame.images, key=lambda i: i.name)
# draw 2d bbox on image of front camera
for i, camera_image in enumerate(images):
if CAMERA_NAME[camera_image.name] == 'front':
image = tf.image.decode_png(camera_image.image)
image = cv2.cvtColor(image.numpy(), cv2.COLOR_RGB2BGR)
print(i,CAMERA_NAME[camera_image.name],image.shape)
# draw the camera labels.
for labels in frame.camera_labels:
if labels.name == camera_image.name:
print(labels.name,camera_image.name)
waymo_utils.draw_labels(image, labels.labels)
cv2.imshow("image{}".format(i),image)
cv2.waitKey()
break
<실행결과>
frame_index =100에 해당하는 프레임에서 영상을 시각화, 이전 예제의 포인트 클라우드와 비교해볼것
9. 과제
(1) tfrecord형식, parquet 형식의 파일포맷을 조사하시오.
|
|
