|
|
1. Open3D-ML 소개
open3d를 이용하여 3D 머신러닝 작업(3d object detection, semantic segmentation)을 수행하는 라이브러리
https://github.com/isl-org/Open3D-ML
2. 가상환경구축
Open3D-ML은 open3d를 설치하면 포함되어 설치되므로 별도로 설치할 필요없음
현재 윈도우즈에서는 Open3D-ML기능을 지원하지 않고 리눅스에서만 지원함
설치환경 : wsl2-ubuntu24.04
cuda, cudnn 설치
wsl2-ubuntu24.04에서 Cuda, cudnn를 사용할때는 윈도우즈에서 설치하면 됨 -> wsl2-ubuntu에서 별도로 설치하면 안됨
윈도우즈에서 최신 GPU driver 설치 -> cuda 최신버전(12.8) 설치 -> cudnn 최신버전(9.8.0) 설치
wsl2-ubuntu24.04에서 anconda 설치
https://www.anaconda.com/docs/getting-started/anaconda/install#macos-linux-installation -> linux installer -> linux x86_64 선택
anaconda 가상환경 open3d-ml 생성
$ conda create --name=open3d-ml python=3.10 -> 파이썬을 다른 버전으로 하면 오류남
가상환경 삭제
$ conda remove --name [가상환경명] --all
anaconda 가상환경 open3d 활성화
$ conda activate open3d-ml
다음 깃허브 안내대로 패키지 설치 -> https://github.com/isl-org/Open3D-ML
$ pip install --upgrade pip
$ pip install open3d
$ wget https://raw.githubusercontent.com/isl-org/Open3D-ML/refs/heads/main/requirements-torch-cuda.txt -> requirement 파일다운로드
$ pip install -r requirements-torch-cuda.txt -> 의존패키지 설치
$ python -c "import open3d.ml.torch as ml3d" -> 설치테스트
numpy관련 오류 해결방법
$ python -c "import open3d.ml.torch as ml3d" 실행시 다음과 같은 오류발생
$ pip install numpy==1.26.4
아래와 같은 오류 발생시 해결방법
$ export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6
아래와 같은 오류 발생시 해결방법
$ export XDG_SESSION_TYPE=x11
참고
$ export GDK_BACKEND=x11
$ export LIBGL_ALWAYS_SOFTWARE=1
3. SemanticKITTI 다운로드
아래 주소에서 Semantic Segmentation 용 3개의 압축파일을 다운받을것 약 80GB임
데이터셋은 윈도우즈 폴더에 다운받아 저장하고 리눅스에서 공유할수 있음
물론 리눅스 폴더로 바로 다운받을수도 있음 -> 사이트에 있는 리눅스 스크립트 파일 참고
https://semantic-kitti.org/dataset.html#download
data_odometry_labels.zip
data_odometry_velodyne.zip
data_odometry_calib.zip
아래 사이트에 가면 위 데이터와 동기화되어 측정된 컬러 영상 data_odometry_color.zip 을 다운가능 -> 영상과 포인트클라우드를 같이 출력할때 사용가능
https://www.cvlibs.net/datasets/kitti/eval_odometry.php
압축을 풀고 data_odometry_labels, data_odometry_velodyne.zip 에 들어있는 파일을 아래 그림과 같은 구조로 재배치하여 저장해야함
dataset->sequences->xx->velodyne(bin), labels(label) -> 2개의 폴더를 하나로 합쳐야함
velodyne폴더 아래의 bin파일 안에 포인트 클라우드 데이터가 저장되어 있음
labels 폴더 아래의 label파일 안에 각 포인트의 클래스 레이블이 저장되어 있음
클래스 레이블 정보
kitti_labels = {
0: 'unlabeled',
1: 'car',
2: 'bicycle',
3: 'motorcycle',
4: 'truck',
5: 'other-vehicle',
6: 'person',
7: 'bicyclist',
8: 'motorcyclist',
9: 'road',
10: 'parking',
11: 'sidewalk',
12: 'other-ground',
13: 'building',
14: 'fence',
15: 'vegetation',
16: 'trunk',
17: 'terrain',
18: 'pole',
19: 'traffic-sign'
}
4. SemanticKITTI 데이터셋 시각화
dataset_path 아래에 dataset->sequences->xx->velodyne(bin), labels(label) 형태로 폴더가 작성되어 있어야 함
포인트 클라우드 파일과 포인트별 클래스 정보를 읽어서 화면에 시각화해줌
클래스별로 색상을 달리하여 구분해줌 -> 우측 설정메뉴에서 확인가능
애니메이션 기능을 제공하여 여러개의 포인트 클라우드 프레임을 순차적으로 그려줌
wsl2-ubuntu에서 윈도우즈 폴더를 공유할 수 있음 -> 별도로 데이터셋이 저장된 윈도우즈 폴더를 리눅스로 복사할 필요없음
윈도우즈 c드라이브 -> /mnt/c/
윈도우즈 d드라이브 -> /mnt/d/
wsl2-ubuntu24.04 -> anaconda 가상환경 생성 -> open3d 설치
<소스코드>
import open3d.ml.torch as ml3d # or open3d.ml.tf as ml3d
dataset_path ='/mnt/d/Users/2sungryul/Dropbox/Work/Dataset/SemanticKITTI/data_odometry_velodyne'
# construct a dataset by specifying dataset_path
dataset = ml3d.datasets.SemanticKITTI(dataset_path)
#print('dataset',dataset)
#dataset = ml3d.datasets.SemanticKITTI()
# get the 'all' split that combines training, validation and test set
all_split = dataset.get_split('all')
# print the attributes of the first datum
print(all_split.get_attr(0))
# print the shape of the first point cloud
print(all_split.get_data(0)['point'].shape)
# show the first 100 frames using the visualizer
vis = ml3d.vis.Visualizer()
vis.visualize_dataset(dataset, 'all', indices=range(300))
<실행결과>
anaconda 가상환경 open3d-ml 에서 실행할 것
오류 메시지가 출력되지만 실행은 됨, cuda, cudnn 버전 문제인것으로 보임 -> 모델 훈련및 추론할때 GPU 이용시에 문제될수 있어 해결해야 함
결과화면에서 우측 properties->labes에 보면 9번 클래스 road는 마젠타색으로, 1번 클래스 car는 살구색으로 그려져 있음
<애니메이션 결과>
위 출력화면에서 우측 Dataset 메뉴에서 list에서 모든 항목을 선택후 animation메뉴에서 start 메뉴클릭
5. SemanticKITTI 데이터셋의 영상과 포인트 클라우드 동시 출력
SemanticKITTI 데이터셋에서는 연속된 장면에 대한 포인트 클라우드와 영상을 제공하고 있음
영상과 semantic segmentation 레이블이 포함된 포인트 클라우드를 동시에 출력함
wsl2-ubuntu24.04 -> anaconda 가상환경 생성 -> open3d 설치
<소스코드>
# visualize both image and point cloud from SemanticKITTI dataset
import open3d.ml.torch as ml3d # or open3d.ml.tf as ml3d
import numpy as np
import open3d as o3d
import cv2
import glob
dataset_path ='/mnt/d/Users/2sungryul/Dropbox/Work/Dataset/SemanticKITTI/data_odometry_velodyne'
image_path=r"/mnt/d/Users/2sungryul/Dropbox/Work/Dataset/SemanticKITTI/data_odometry_color/dataset/sequences/00/image_2"
label_to_names = {
0: 'unlabeled',
1: 'car',
2: 'bicycle',
3: 'motorcycle',
4: 'truck',
5: 'other-vehicle',
6: 'person',
7: 'bicyclist',
8: 'motorcyclist',
9: 'road',
10: 'parking',
11: 'sidewalk',
12: 'other-ground',
13: 'building',
14: 'fence',
15: 'vegetation',
16: 'trunk',
17: 'terrain',
18: 'pole',
19: 'traffic-sign'
}
Colors = [[0., 0., 0.], [0.96078431, 0.58823529, 0.39215686],
[0.96078431, 0.90196078, 0.39215686],
[0.58823529, 0.23529412, 0.11764706],
[0.70588235, 0.11764706, 0.31372549], [1., 0., 0.],
[0.11764706, 0.11764706, 1.], [0.78431373, 0.15686275, 1.],
[0.35294118, 0.11764706, 0.58823529], [1., 0., 1.],
[1., 0.58823529, 1.], [0.29411765, 0., 0.29411765],
[0.29411765, 0., 0.68627451], [0., 0.78431373, 1.],
[0.19607843, 0.47058824, 1.], [0., 0.68627451, 0.],
[0., 0.23529412,
0.52941176], [0.31372549, 0.94117647, 0.58823529],
[0.58823529, 0.94117647, 1.], [0., 0., 1.], [1.0, 1.0, 0.25],
[0.5, 1.0, 0.25], [0.25, 1.0, 0.25], [0.25, 1.0, 0.5],
[0.25, 1.0, 1.25], [0.25, 0.5, 1.25], [0.25, 0.25, 1.0],
[0.125, 0.125, 0.125], [0.25, 0.25, 0.25], [0.375, 0.375, 0.375],
[0.5, 0.5, 0.5], [0.625, 0.625, 0.625], [0.75, 0.75, 0.75],
[0.875, 0.875, 0.875]]
print(len(Colors))
print(Colors[0])
for filename in glob.glob(image_path+'/'+'*.png'):
break
image = cv2.imread(filename)
cv2.imshow("Image", image)
cv2.waitKey()
# construct a dataset by specifying dataset_path
dataset = ml3d.datasets.SemanticKITTI(dataset_path)
#print('dataset',dataset)
#dataset = ml3d.datasets.SemanticKITTI()
# get the 'all' split that combines training, validation and test set
all_split = dataset.get_split('train')
print(len(all_split))
# print the attributes of the first datum
print(all_split.get_attr(0))
# print the shape of the first point cloud
print(all_split.get_data(0).keys())
print(all_split.get_data(0)['point'].shape)
print(all_split.get_data(0)['label'].shape)
print(all_split.get_data(0)['feat'].shape)
print(all_split.get_data(0)['label'])
colorlist = []
for label in all_split.get_data(0)['label']:
colorlist.append(Colors[label])
print(len(colorlist))
# create open3d tensor
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(all_split.get_data(0)['point'])
pcd.colors = o3d.utility.Vector3dVector(colorlist)
# create open3d window
vis = o3d.visualization.Visualizer()
vis.create_window(window_name='semantickitti',width=1241, height=376)
# set background_color and point_size
vis.get_render_option().background_color = np.asarray([0,0,0]).astype(float)
vis.get_render_option().point_size = 2
# add lidar axis
#axis_pcd = o3d.geometry.TriangleMesh.create_coordinate_frame(size=3.0, origin=[0, 0, 0])
#vis.add_geometry(axis_pcd)
# add point cloud
vis.add_geometry(pcd)
# set zoom, front, up, and lookat
vis.get_view_control().set_zoom(0.02)
vis.get_view_control().set_front([-2, 0, 1])
vis.get_view_control().set_up([1, 0, 1])
vis.get_view_control().set_lookat([-10, 0, 0])
vis.run()
vis.destroy_window()
<실행결과>
6. SemanticKITTI 데이터셋의 포인트 클라우드 애니메이션
SemanticKITTI 데이터셋에서는 연속된 장면에 대한 포인트 클라우드를 애니메이션하는 코드
wsl2-ubuntu24.04 -> anaconda 가상환경 생성 -> open3d 설치
<소스코드>
# ----------------------------------------------------------------------------
# - Open3D: www.open3d.org -
# ----------------------------------------------------------------------------
# Copyright (c) 2018-2024 www.open3d.org
# SPDX-License-Identifier: MIT
# ----------------------------------------------------------------------------
# semantickitti dataset animation example
import open3d.ml.torch as ml3d # or open3d.ml.tf as ml3d
import numpy as np
import open3d as o3d
import threading
import time
import cv2
import glob
dataset_path ='/mnt/d/Users/2sungryul/Dropbox/Work/Dataset/SemanticKITTI/data_odometry_velodyne'
image_path=r"/mnt/d/Users/2sungryul/Dropbox/Work/Dataset/SemanticKITTI/data_odometry_color/dataset/sequences/00/image_2"
label_to_names = {
0: 'unlabeled',
1: 'car',
2: 'bicycle',
3: 'motorcycle',
4: 'truck',
5: 'other-vehicle',
6: 'person',
7: 'bicyclist',
8: 'motorcyclist',
9: 'road',
10: 'parking',
11: 'sidewalk',
12: 'other-ground',
13: 'building',
14: 'fence',
15: 'vegetation',
16: 'trunk',
17: 'terrain',
18: 'pole',
19: 'traffic-sign'
}
Colors = [[0., 0., 0.], [0.96078431, 0.58823529, 0.39215686],
[0.96078431, 0.90196078, 0.39215686],
[0.58823529, 0.23529412, 0.11764706],
[0.70588235, 0.11764706, 0.31372549], [1., 0., 0.],
[0.11764706, 0.11764706, 1.], [0.78431373, 0.15686275, 1.],
[0.35294118, 0.11764706, 0.58823529], [1., 0., 1.],
[1., 0.58823529, 1.], [0.29411765, 0., 0.29411765],
[0.29411765, 0., 0.68627451], [0., 0.78431373, 1.],
[0.19607843, 0.47058824, 1.], [0., 0.68627451, 0.],
[0., 0.23529412,
0.52941176], [0.31372549, 0.94117647, 0.58823529],
[0.58823529, 0.94117647, 1.], [0., 0., 1.], [1.0, 1.0, 0.25],
[0.5, 1.0, 0.25], [0.25, 1.0, 0.25], [0.25, 1.0, 0.5],
[0.25, 1.0, 1.25], [0.25, 0.5, 1.25], [0.25, 0.25, 1.0],
[0.125, 0.125, 0.125], [0.25, 0.25, 0.25], [0.375, 0.375, 0.375],
[0.5, 0.5, 0.5], [0.625, 0.625, 0.625], [0.75, 0.75, 0.75],
[0.875, 0.875, 0.875]]
CLOUD_NAME = "points"
FRAME_NUM = 1000
class MultiWinApp:
def __init__(self):
self.is_done = False
self.cloud = None
self.main_vis = None
self.frame_index = 0
self.first = False
self.bbox_num = 0
#self.n_snapshots = 0
#self.snapshot_pos = None
# construct a dataset by specifying dataset_path
dataset = ml3d.datasets.SemanticKITTI(dataset_path)
#print('dataset',dataset)
# get the 'all' split that combines training, validation and test set
self.all_split = dataset.get_split('train')
print('train dataset size:',len(self.all_split))
# print the attributes of the first datum
#print(all_split.get_attr(0))
# print the shape of the first point cloud
print(self.all_split.get_data(0).keys())
print(self.all_split.get_data(0)['point'].shape)
print(self.all_split.get_data(0)['label'].shape)
print(self.all_split.get_data(0)['feat'].shape)
#print(all_split.get_data(0)['label'])
#colorlist = []
#for label in self.all_split.get_data(0)['label']:
# colorlist.append(Colors[label])
#print('colorlist size:',len(colorlist))
def run(self):
app = o3d.visualization.gui.Application.instance
app.initialize()
self.main_vis = o3d.visualization.O3DVisualizer("semantickitti", width=1241, height=376)
self.main_vis.reset_camera_to_default()
#self.main_vis.setup_camera(80, [0, 0, 0], [-15, 0, 10], [5, 0, 10]) # center, eye, up
self.main_vis.setup_camera(100, [0, 0, 0], [-10, 0, 7], [1, 0, 1]) # center, eye, up
self.main_vis.set_background(np.array([0, 0, 0, 0]), None)
self.main_vis.show_skybox(False)
self.main_vis.point_size = 2
self.main_vis.show_settings = False
self.main_vis.set_on_close(self.on_main_window_closing)
app.add_window(self.main_vis)
threading.Thread(target=self.update_thread).start()
#self.main_vis.add_action("Take snapshot in new window", self.on_snapshot)
#self.snapshot_pos = (self.main_vis.os_frame.x, self.main_vis.os_frame.y)
app.run()
def on_main_window_closing(self):
self.is_done = True
return True # False would cancel the close
def update_thread(self):
# This is NOT the UI thread, need to call post_to_main_thread() to update
# the scene or any part of the UI.
# Initialize point cloud geometry
point_cloud = o3d.geometry.PointCloud()
while not self.is_done:
time.sleep(1)
def update_cloud():
print("frame_index:",self.frame_index,self.all_split.get_data(self.frame_index)['point'].shape)
if self.first:
self.main_vis.remove_geometry("axis")
self.main_vis.remove_geometry("pc")
axis_pcd = o3d.geometry.TriangleMesh.create_coordinate_frame(size=1.6, origin=[0, 0, 0])
self.main_vis.add_geometry("axis", axis_pcd)
# Update point cloud with new data
point_cloud.points = o3d.utility.Vector3dVector(self.all_split.get_data(self.frame_index)['point'])
colorlist = []
for label in self.all_split.get_data(self.frame_index)['label']:
colorlist.append(Colors[label])
point_cloud.colors = o3d.utility.Vector3dVector(colorlist)
self.main_vis.add_geometry("pc", point_cloud)
# save screen image to jpg
self.main_vis.export_current_image("pc_%06d.png" % self.frame_index)
# Move to the next frame
self.frame_index = (self.frame_index + 1) % FRAME_NUM
self.first = True
o3d.visualization.gui.Application.instance.post_to_main_thread(self.main_vis, update_cloud)
if self.is_done: # might have changed while sleeping
break
def main():
MultiWinApp().run()
if __name__ == "__main__":
main()
<실행결과>
|
|
