KITTI의 LiDAR 포인트 클라우드에 3D 큐보이드 라벨링하기
Potato에서 KITTI LiDAR 스캔에 카메라 확인과 함께 박스를 그리고, 어노테이터 간 3D IoU 일치도를 측정하고, KITTI devkit 형식으로 라벨 파일을 내보냅니다.
자율 주행과 로보틱스 팀은 이 튜토리얼을 따라 Potato 2.13.1에서 KITTI LiDAR 스캔에 3D 박스를 그리고, 모든 박스를 카메라 이미지와 대조하고, 두 어노테이터 사이의 일치도를 측정하고, KITTI 트래킹 라벨을 편집 가능한 트랙으로 가져오고, KITTI 라벨 파일을 내보낼 수 있습니다. LiDAR 센서의 포인트 클라우드는 3D 점의 집합이며, 각 점은 미터 단위의 위치와 반사 강도를 가집니다. 큐보이드는 중심, 길이, 너비, 높이, 방향(heading)을 가진 방향성 3D 경계 상자입니다.
실제 사례는 KITTI 3D 객체 벤치마크의 스캔 두 개와 KITTI 트래킹 벤치마크의 한 시퀀스에서 처음 10프레임을 사용합니다. 이 페이지의 수치는 그 파일들에 대해 스크립트로 작동하는 어노테이터 두 명과 헤드리스 브라우저로 Potato 2.12.1을 실행한 결과에서 나왔습니다. 가져오기와 내보내기는 저장된 어노테이션에 대해 Potato 2.13.1로 다시 실행했으며, 여기에 보이는 가져오기와 내보내기 출력은 2.13.1의 것입니다.
KITTI 데이터
KITTI는 Velodyne LiDAR와 카메라 네 대를 단 자동차가 카를스루에를 돌아다니며 기록한 것입니다(Geiger et al., 2013). 3D 객체 벤치마크(Geiger et al., 2012)에는 학습 스캔 7,481개와 테스트 스캔 7,518개가 있으며, 각각 왼쪽 컬러 이미지와 보정 파일이 딸려 있습니다. 트래킹 벤치마크에는 라벨이 있는 학습 시퀀스가 21개 있습니다. KITTI 사이트는 모든 KITTI 데이터셋을 CC BY-NC-SA 3.0 아래 공개하며 데이터가 "made available for academic use only"라고 밝힙니다. KITTI 스캔이나 라벨을 상업적 작업에 쓸 수 없고, CC BY-NC-SA는 공유하는 변형물에 같은 라이선스를 적용하도록 요구합니다. 이 튜토리얼은 KITTI 파일을 하나도 포함하지 않으므로, KITTI 사이트에 로그인한 뒤 직접 파일을 내려받으세요.
객체 벤치마크 페이지에서 Velodyne 포인트 클라우드(29GB), 왼쪽 컬러 이미지(12GB), 카메라 보정 행렬(16MB), 학습 라벨(5MB)을 내려받으세요. 트래킹 벤치마크 페이지에서는 Velodyne 포인트 클라우드, 카메라 보정, 학습 라벨을 내려받으세요. 이 튜토리얼은 학습 스캔 000063과 000151, 그리고 트래킹 시퀀스 0000의 프레임 000000부터 000009까지만 사용하며, 구성은 다음과 같습니다.
kitti-lidar/
├── kitti_object/training/
│ ├── velodyne/000063.bin, 000151.bin
│ ├── image_2/000063.png, 000151.png
│ ├── calib/000063.txt, 000151.txt
│ └── label_2/000063.txt, 000151.txt
└── kitti_tracking/training/
├── velodyne/0000/000000.bin ... 000009.bin
├── calib/0000.txt
└── label_02/0000.txtKITTI의 .bin 스캔은 점당 32비트 부동소수점 네 개(x, y, z, 강도)로 이루어진 헤더 없는 목록입니다. 스캔 000063에는 점이 119,796개, 스캔 000151에는 115,174개 있습니다. 두 스캔에 대한 KITTI의 라벨은 객체 여덟 개를 나열합니다.
| 스캔 | KITTI 라벨 파일의 객체 | 비고 |
|---|---|---|
| 000063 | Car 2, Pedestrian 1, Van 1 | Van은 앞쪽 34.8m에 있음 |
| 000151 | Car 2, Cyclist 1, Pedestrian 1, DontCare 3 | Car 하나와 Pedestrian이 이미지 가장자리에서 잘림 |
이 튜토리얼에서 KITTI의 라벨은 정답 키입니다. 어노테이터는 KITTI의 라벨을 보지 못하며, 아래 테스트 실행은 각 어노테이터의 박스를 KITTI의 박스와 비교합니다.
Potato 설치
포인트 클라우드 뷰어는 기본 패키지 외에 아무것도 필요하지 않습니다.
pip install potato-annotation==2.13.1Potato는 서버에서 .bin 파일을 읽고, 뷰어의 3D 라이브러리는 패키지 안에 들어 있습니다. 헤드리스 브라우저 테스트에서 어노테이션 페이지는 Potato 서버 외의 어떤 호스트에도 요청을 보내지 않았습니다.
프로젝트 폴더
첫 프로젝트 kitti-boxes는 객체 벤치마크의 스캔 두 개를 어노테이터 두 명에게 줍니다. 각 스캔의 .bin, .png, 보정 파일을 media/ 폴더에 복사하고, 데이터 파일에 한 줄에 스캔 하나씩 나열합니다.
kitti-boxes/
├── config.yaml
├── data/
│ └── scans.jsonl
└── media/
├── velodyne/000063.bin, 000151.bin
├── image_2/000063.png, 000151.png
└── calib/000063.txt, 000151.txtpoint_cloud는 media/ 기준으로 스캔을 지정하고, calibration은 KITTI의 보정 파일을 왼쪽 컬러 이미지(KITTI의 카메라 P2)와 함께 지정합니다.
{"id": "000063", "text": "KITTI 3D object training scan 000063", "point_cloud": "velodyne/000063.bin", "calibration": {"file": "calib/000063.txt", "images": {"P2": "image_2/000063.png"}}}
{"id": "000151", "text": "KITTI 3D object training scan 000151", "point_cloud": "velodyne/000151.bin", "calibration": {"file": "calib/000151.txt", "images": {"P2": "image_2/000151.png"}}}어노테이터에게 KITTI의 정답이 필요하지 않으므로 label_2 파일은 프로젝트에 넣지 않습니다.
설정
아래 설정은 완전하며 2.12.1과 2.13.1에서 python -m potato.validate_cli config.yaml --strict를 통과합니다.
annotation_task_name: KITTI 3D boxes, two annotators
task_dir: .
output_annotation_dir: annotation_output/
media_directory: media
data_files:
- data/scans.jsonl
item_properties:
id_key: id
text_key: text
user_config:
allow_all_users: true
users: []
# Both annotators box every scan
num_annotators_per_item: 2
annotation_schemes:
- annotation_type: spatial_annotation
name: objects
description: Put a 3D box around every car, van, pedestrian, and cyclist
source_field: point_cloud
calibration_field: calibration
tools: [cuboid_3d]
# KITTI's own class names, so the export writes the names unchanged
labels:
- {name: Car, color: "#FF6B6B", key_value: "1"}
- {name: Van, color: "#4ECDC4", key_value: "2"}
- {name: Pedestrian, color: "#FFD93D", key_value: "3"}
- {name: Cyclist, color: "#6C8AE4", key_value: "4"}
attributes:
- name: occluded
type: select
options: [none, partly, largely, unknown]
default: none
default_box_height: 1.5
fit_box_height: true네 가지 설정에는 이유가 있습니다.
- 클래스 이름. KITTI 내보내기는 각 박스의 클래스 이름을 라벨 파일의
type열로 씁니다. 클래스를Car,Van,Pedestrian,Cyclist로 이름 지으면 KITTI의 도구가 매핑 단계 없이 읽는 줄이 만들어집니다. calibration_field. 보정은 뷰어 아래의 카메라 패널을 켜며, 내보내기는 KITTI의 카메라 좌표를 쓰기 위해 보정이 필요합니다. 내보내기는 보정이 없는 항목을 건너뛰고 경고에 그 항목을 명시합니다.occluded속성. 네 옵션은 내보내기에서 KITTI의 가림 코드 0에서 3에 대응합니다. 내보내기가 아래 내보내기 단계에서 설명하듯 카메라 이미지에서 잘림을 측정하므로, 설정에는 잘림 속성이 없습니다.default_box_height와fit_box_height. 새 박스는 높이 1.5m에서 시작하며, 이는 두 스캔의 승용차 네 대의 1.45에서 1.59m에 가깝습니다.fit_box_height: true이면 뷰어는 박스의 바닥 면적에 점이 다섯 개 이상 있을 때마다 박스의 위와 아래를 그 면적 안의 LiDAR 점에 맞춥니다.
박스 그리기
프로젝트 폴더에서 서버를 시작합니다.
potato start config.yaml -p 8000각 어노테이터는 서버 주소를 열고 사용자 이름과 비밀번호로 등록하면 스캔 두 개를 모두 받습니다. 뷰어는 높이에 따라 색을 입힌 클라우드를 보여 주며, 스캔 000063 전체가 도착하면 클라우드 아래의 상태 줄에 "119,796 points, all loaded"라고 표시됩니다. 클라우드 아래에는 두께 2.0m의 직교 슬랩 패널 세 개, 즉 Top (X-Y), Front (Y-Z), Side (X-Z)와 왼쪽 컬러 이미지가 있는 카메라 패널이 있습니다.
3D 박스 도구는 c를, 클래스는 숫자 키(Car는 1)를 누른 다음, 지면에서 객체의 바닥 면적 위로 끌어 선택합니다. 상태 줄은 박스의 크기와 위치로 박스를 확인해 주며, 브라우저 테스트에서는 예를 들어 "Box added: 7.9 x 0.4 x 1.5 m at 34.3, -19.2"라고 표시되었습니다. 새 박스는 수평이며 LiDAR의 축에 정렬되어 있습니다. q와 e를 누르면 선택한 박스가 5도 단위로 돌고, Shift+q와 Shift+e는 1도 단위입니다. 슬랩 패널에서는 화살표 키가 박스를 움직이고, Shift와 화살표는 그쪽 면을 바깥으로, Alt와 화살표는 안쪽으로 움직입니다. ,와 . 키는 스캔의 박스를 차례로 이동하고, Delete는 선택한 박스를 제거합니다. 선택한 박스의 가림 메뉴는 아래쪽 속성 패널에 있습니다.
카메라 패널은 스캔의 보정을 통해 모든 박스를 왼쪽 컬러 이미지 위에 그립니다. 위에서 보면 맞아 보이는 박스도 시선 방향으로는 너무 짧을 수 있으며, 카메라 이미지는 그 오류를 바로 보여 줍니다. 다음을 클릭하기 전에 카메라 패널에서 모든 박스를 확인하세요.
클래스 버튼은 다음 박스의 클래스를 정합니다. Potato 2.13.1부터는 박스를 선택한 상태에서 클래스 버튼을 클릭하면 그 박스의 클래스가 바뀌고, 시퀀스에서는 트랙 전체의 클래스가 바뀝니다. 2.12.1의 브라우저 테스트에서는 클릭이 대기 중인 클래스만 바꿨고, 클래스가 틀린 박스는 삭제하고 다시 그려야 했습니다.
이 페이지의 바탕이 된 테스트 실행
스크립트로 작동하는 어노테이터 두 명이 실행 중인 서버를 통해 두 스캔에 박스를 그리고, 뷰어가 보내는 것과 같은 요청으로 각 스캔을 저장했습니다. 각 어노테이터는 모든 객체에 대해 KITTI의 라벨에서 출발했습니다. 스크립트는 Potato의 KITTI 변환으로 각 라벨을 LiDAR 프레임으로 바꾼 다음 박스를 정해진 방식으로 바꿨습니다.
- ana는 모든 중심을 앞쪽으로 0.10m, 오른쪽으로 0.05m 옮기고, 모든 변을 0.05m 늘리고, 모든 방향을 2도 돌리고, KITTI의 가림 수준을 그대로 복사했습니다.
- ben은 모든 중심을 뒤쪽으로 0.25m, 왼쪽으로 0.15m 옮기고, 모든 변을 8% 늘리고, 모든 방향을 -6도 돌리고, 스캔 000063에서 34.8m 떨어진 Van을 건너뛰고, 스캔 000151의 Cyclist를 Pedestrian이라 부르고, 가림을
none으로 두었습니다.
고정된 오류 덕분에 일치도 수치를 KITTI의 라벨과 대조해 확인할 수 있습니다. 사람의 오류는 스크립트로 만든 두 패턴보다 박스마다 더 크게 달라집니다.
어노테이터 간 일치도
브라우저에서 /admin을 열고, 프로젝트 폴더의 admin_api_key.txt에 있는 관리자 키를 입력한 다음 /admin/iaa를 엽니다. 테스트 실행에서 보고서는 두 스캔과 두 어노테이터를 모두 나열하고 다음을 제시했습니다.
mean_agreement 0.272
mean_object_count_diff 0.500
attributes.occluded.alpha -0.174
attributes.occluded.percent_agreement 0.400Potato는 각 스캔에서 두 어노테이터의 박스를 3D IoU(교집합 대비 합집합)로 짝지으며, 회전된 두 박스의 IoU를 정확하게 계산합니다. 3D IoU가 0.25 이상이면 두 박스를 같은 객체로 칩니다. 스캔의 점수는 짝지어진 쌍의 IoU 합을 짝지어진 쌍의 수와 짝이 없는 박스의 수의 합으로 나눈 값입니다. 클래스가 다른 쌍은 그 쌍의 IoU의 절반으로 계산합니다. 두 스캔은 0.239(000063)와 0.305(000151)를 기록했고, 보고서는 두 점수의 평균을 제시합니다. 아래 표는 이 점수의 바탕이 된 쌍과, KITTI의 라벨에 대한 각 박스의 3D IoU를 나열합니다.
| 스캔 | 객체 | ana와 ben | ana와 KITTI | ben과 KITTI |
|---|---|---|---|---|
| 000063 | Car | 0.664 | 0.870 | 0.705 |
| 000063 | Car | 0.532 | 0.823 | 0.595 |
| 000063 | Pedestrian | 0.25 미만, 짝 없음 | 0.576 | 0.237 |
| 000063 | Van | ben이 박스를 그리지 않음 | 0.886 | 0.000 |
| 000151 | Car | 0.657 | 0.864 | 0.698 |
| 000151 | Cyclist | 0.556, 클래스가 다름 | 0.806 | 0.609 |
| 000151 | Pedestrian | 0.25 미만, 짝 없음 | 0.632 | 0.307 |
| 000151 | Car | 0.593 | 0.846 | 0.647 |
두 보행자는 작은 객체에서 3D IoU가 얼마나 엄격한지 보여 줍니다. 보행자 박스는 폭이 0.5에서 1.0m이고 ana와 ben의 중심은 0.40m 떨어져 있습니다. 두 박스는 공유하는 부피가 너무 작아 짝지어지지 않고, 각 박스는 짝 없는 객체로 계산됩니다. 길이 3.2에서 4.5m인 승용차에서는 같은 0.40m 차이에도 0.53에서 0.66으로 짝지어졌습니다. 가림 알파 -0.174는 짝지어진 박스 다섯 개를 대상으로 합니다. Ben의 일괄 none은 ana가 복사한 KITTI 수준과 다섯 개 중 두 개에서 일치했습니다.
mean_agreement 값은 우연 보정이 되어 있지 않으며, 보고서는 쌍별이나 어노테이터별 내역을 제공하지 않습니다. 3D 박스, 시간 구간, 에이전트 트레이스의 일치도가 짝짓기를 더 자세히 설명합니다.
KITTI 라벨 파일 내보내기
kitti_3d 내보내기는 스캔마다 KITTI 라벨 파일 하나를 쓰며, 저장한 어노테이터가 둘 이상이면 어노테이터마다 폴더를 둡니다.
python -m potato.export -c config.yaml -f kitti_3d -o export/export/ana/label_2/000063.txt
export/ana/label_2/000151.txt
export/ben/label_2/000063.txt
export/ben/label_2/000151.txt각 줄에는 KITTI의 15개 필드가 있으며, 스캔 000151에 대한 ana의 파일은 다음과 같습니다.
Car 0.95 0 -0.87 0.00 199.35 312.43 369.00 1.55 1.83 3.74 -3.12 1.69 3.45 -1.60
Cyclist 0.00 1 -1.63 990.09 145.93 1031.72 217.23 1.77 0.83 1.76 10.57 0.91 18.52 -1.11
Pedestrian 0.84 0 -0.99 1210.27 127.60 1223.00 228.51 1.77 0.60 0.98 11.62 0.84 12.81 -0.25
Car 0.00 1 -0.43 845.53 161.27 1005.56 210.34 1.50 1.80 4.28 10.08 0.91 22.52 0.00Potato는 박스를 LiDAR 프레임의 미터 단위로 저장하며 회전은 쿼터니언입니다. KITTI의 3D 필드는 보정된 기준 카메라의 프레임에 있습니다. 내보내기는 스캔의 보정 파일에 있는 R0_rect와 Tr_velo_to_cam으로 각 박스를 변환하고, 위치를 박스의 중심에서 박스 바닥면의 중심으로 옮기며, 방향을 rotation_y로 씁니다. KITTI는 방향만 저장하므로, 내보내기는 피치와 롤을 버리고 1도 넘게 기울어진 박스가 몇 개였는지 경고로 보고합니다.
변환을 확인하기 위해 세 번째 스크립트 어노테이터가 두 스캔에서 KITTI의 라벨을 바꾸지 않고 저장했고, 내보낸 파일을 KITTI의 label_2 파일과 필드 단위로 비교했습니다.
| 필드 | Potato가 쓰는 것 | KITTI 파일과의 비교 |
|---|---|---|
| type | 박스의 클래스 이름 | 같음 |
| truncated | 박스의 카메라 투영 중 이미지 밖에 있는 비율 | 0.01 이내(잘린 두 객체에서 0.96과 0.88, KITTI는 0.96과 0.87) |
| occluded | occluded 속성을 코드 0에서 3으로 | 같음 |
| alpha | rotation_y에서 객체 방향을 뺀 값 | 0.05 rad 이내 |
| 2D box | P2로 투영하고 이미지에 맞게 자른 3D 박스 | 5.7px 이내 |
| h, w, l | 박스의 크기 | 같음 |
| x, y, z | 카메라 좌표의 바닥면 중심 | 같음 |
| rotation_y | 카메라 좌표의 방향 | 같음 |
| score | 쓰지 않음 | 해당 없음 |
2D 박스는 이미지에 그린 박스가 아니라 큐보이드의 투영이며, 스캔 000063의 보행자에서는 투영이 KITTI의 손으로 그린 박스보다 넓게 나옵니다(왼쪽 가장자리에서 5.7px). 이미지 가장자리에서 잘린 객체에서는 Potato 2.13.1이 박스를 마지막 픽셀에서 자르므로, 스캔 000151의 보행자는 KITTI의 파일처럼 1223.00에서 끝납니다. Potato 2.12.1은 이미지 너비에서 잘라 1224.00을 썼습니다. Potato에는 DontCare 영역 유형이 없으므로 내보내기는 DontCare 줄을 쓰지 않습니다. KITTI의 객체 라벨과 내보내기 모두 소수점 둘째 자리를 쓰므로 3D 필드는 바뀌지 않은 채로 돌아왔습니다.
KITTI 라벨 가져오기
potato import -f kitti는 KITTI의 label_2 파일을 2D 이미지 박스 프로젝트로 읽습니다. 가져오기는 각 객체의 크기, 위치, rotation_y를 읽기 전용 속성으로 유지하고, 2D 박스만 편집할 수 있다는 경고를 출력합니다. Potato 2.13.1에는 KITTI의 3D 객체 라벨을 단일 스캔에 큐보이드로 배치하는 가져오기가 없습니다.
KITTI의 트래킹 라벨은 큐보이드로 들어옵니다. potato import -f kitti_tracking은 label_02/, velodyne/, calib/가 있는 트래킹 폴더를 읽고, 시퀀스당 항목 하나와 라벨이 있는 프레임마다 키프레임 하나를 만듭니다. kitti-lidar 폴더에서 가져오기를 실행하세요.
potato import -i kitti_tracking/training -f kitti_tracking -o tracking-0000INFO: Marked 31 imported annotation(s) as source='import' (kitti_tracking)
WARNING: Left out 17 DontCare row(s): they mark regions KITTI excludes from evaluation, not objects.
Imported 1 sequence(s), 4 track(s), 31 keyframe(s), 3 categor(ies) into tracking-0000
Run it:
potato start tracking-0000/config.yaml -p 8000테스트 실행은 내려받은 스캔 10개에 맞추기 위해 프레임 0에서 9의 라벨 행만 남겼습니다. 이 10프레임에는 트랙이 네 개 있으며, 프레임 0부터 있는 Van, Cyclist, Pedestrian과 프레임 5부터 있는 두 번째 Van입니다. 가져오기는 시퀀스의 보정 파일을 통해 모든 박스를 KITTI의 카메라 프레임에서 LiDAR 프레임으로 변환하며, Potato는 스캔의 순서로 프레임에 번호를 매기므로 스캔이 프레임 번호를 건너뛰는 시퀀스는 제외합니다. 스킴 이름은 objects입니다. Potato 2.12.1은 명령에 --schema-name objects를 추가하지 않으면 스킴 이름을 image_annotation으로 지었습니다.
가져오기는 tracking-0000/config.yaml, data/training.json, README를 씁니다. 생성된 설정은 media_directory를 데이터셋 폴더로 다시 가리키고, 가져온 트랙을 모든 어노테이터에게 출발점으로 보여 줍니다. 아래 설정은 생성된 설정에 네 가지를 바꾼 것이며, 완전하고 검증기를 통과합니다.
annotation_task_name: KITTI tracking 0000, two annotators
task_dir: .
output_annotation_dir: annotation_output/
media_directory: ../kitti_tracking/training
data_files:
- data/training.json
item_properties:
id_key: id
text_key: notes
user_config:
allow_all_users: true
users: []
num_annotators_per_item: 2
pre_annotation:
enabled: true
field: predictions
allow_modification: true
annotation_schemes:
- annotation_type: spatial_annotation
name: objects
description: Correct the imported tracks
source_field: point_cloud
calibration_field: calibration
tools: [cuboid_3d]
labels:
- {name: Van, color: "#4ECDC4", key_value: "1"}
- {name: Pedestrian, color: "#FFD93D", key_value: "2"}
- {name: Cyclist, color: "#6C8AE4", key_value: "3"}
attributes:
- name: occluded
type: select
options: [none, partly, largely, unknown]
default: none
per_keyframe: true
- name: truncated
type: select
options: [none, partly, fully]
default: none
per_keyframe: true바뀐 것은 작업 이름, num_annotators_per_item: 2, 그리고 클래스별 색과 키입니다. KITTI는 모든 프레임에 가림과 잘림을 기록하고 가져오기는 각 프레임의 값을 쓰므로, 2.13.1의 가져오기는 두 속성에 per_keyframe: true도 씁니다. per_keyframe이 없으면 한 프레임에서 속성을 바꾼 것이 트랙의 모든 키프레임에 적용되며, Potato 2.12.1에서는 이 설정을 직접 추가해야 합니다. Potato 2.12.1의 검증기는 pre_annotation이 Potato 3.0에서 제거될 예정이라는 안내도 출력했으며, 2.13.1은 안내를 출력하지 않습니다.
트랙 교정
이전처럼 tracking-0000에서 서버를 시작합니다. 클라우드 아래의 프레임 막대에는 "Frame 1 of 10 (3 keyframes)"가 표시되고, [와 ]는 프레임을 이동합니다. 트랙의 박스는 트랙의 키프레임에만 저장되며, 두 키프레임 사이의 프레임에서는 뷰어가 박스를 보간해(중심과 크기는 선형으로, 회전은 최단 호를 따라) 점선 가장자리로 그립니다. 보간된 박스를 옮기거나 크기를 바꾸거나 회전하면 그 위치에 새 키프레임이 만들어집니다. f는 선택한 박스를 다음 프레임에 키프레임으로 복사하고, Delete는 현재 프레임에서 선택한 박스의 키프레임을 제거하며, 트랙 삭제 버튼은 트랙 전체를 제거합니다. Potato는 키프레임 사이에서 객체를 따라가도록 박스를 옮기지 않습니다. 모든 키프레임은 어노테이터가 배치했거나 가져온 박스입니다.
트래킹 보정 파일에는 이미지가 지정되어 있지 않으므로, 카메라 패널은 사진 없이 박스 투영만 보여 줍니다. Potato의 보정은 항목에 카메라당 이미지 하나를 지정하므로, 시퀀스는 각 프레임 자체의 사진을 보여 줄 수 없습니다.
시퀀스의 일치도와 내보내기
스크립트 어노테이터 두 명이 더 가져온 트랙을 저장했습니다. eli는 가져온 키프레임 31개를 모두 바꾸지 않고 저장했습니다. dana는 각 트랙의 키프레임을 프레임 0, 3, 6, 9와 트랙의 첫 프레임과 마지막 프레임에 남기고 나머지 17개를 삭제했으므로, Potato는 그 사이 프레임의 박스를 보간합니다. /admin/iaa는 보간된 박스를 채워 시퀀스를 프레임별로 점수화하고 트랙 동일성 척도를 추가합니다.
mean_agreement 0.991
n_frames 10
identity.mota 1.0
identity.idf1 1.0
identity.id_switches 0
attributes.occluded.alpha 1.0
attributes.truncated.alpha 0.745보간된 박스는 이 도로 구간에서 KITTI의 라벨에 가깝게 유지되었습니다. 같은 프레임의 KITTI 박스와 비교하면 dana가 내보낸 박스는 평균 중심 오차 0.005m, 최대 오차 0.051m였고, 3D IoU는 최저 0.876, 평균 0.991이었습니다. 키프레임 사이에서 회전하거나 제동하는 차량은 더 큰 오차를 남길 수 있으므로, 움직임이 바뀌는 곳에 키프레임을 추가하세요.
트래킹 보정 파일에는 이미지 크기가 기록되지 않으므로 KITTI 이미지의 크기를 내보내기에 전달하세요.
python -m potato.export -c config.yaml -f kitti_3d -o export/ --option image_width=1242 --option image_height=375내보내기는 KITTI의 트래킹 형식으로 export/eli/label_02/0000.txt와 export/dana/label_02/0000.txt를 쓰며, 15개 객체 필드 앞에 프레임 번호와 트랙 ID가 붙고 모든 트랙의 모든 프레임마다 한 줄이 있습니다. 이미지 크기가 없으면 내보내기는 2D 박스를 자르지 않고 쓰며 경고로 이를 알립니다. eli의 파일 첫 세 줄은 다음과 같습니다.
0 0 Van 0 0 -1.788236 297.306825 163.588132 455.254746 294.245211 2.000000 1.823255 4.433886 -4.552284 1.858523 13.410495 -2.115488
0 1 Cyclist 0 0 -1.952163 739.947686 163.364720 931.778054 374.000000 1.739063 0.824591 1.785241 1.640400 1.675660 5.776261 -1.675458
0 2 Pedestrian 0 0 -2.540723 1088.811242 167.172676 1220.919879 324.237147 1.714062 0.767881 0.972283 6.301919 1.652419 8.455685 -1.900245KITTI의 label_02 파일과 비교하면 eli의 31줄은 트랙, 유형, 잘림, 가림, 크기, 위치, rotation_y가 일치했고, 바뀌지 않은 박스는 KITTI의 박스에 대해 3D IoU 1.000으로 돌아왔습니다. Potato 2.13.1은 KITTI의 트래킹 라벨처럼 소수점 아래 여섯 자리를 쓰며, 누군가 박스를 편집하지 않는 한 가져온 박스 자체의 잘림을 유지합니다. Potato 2.12.1은 소수점 아래 두 자리를 써서 바뀌지 않은 박스의 3D IoU가 0.979 이상이었습니다. Potato 2.12.1은 또한 박스의 투영으로 잘림을 측정해, KITTI가 0인 31줄 중 13줄(10개 프레임 모두에서 박스가 이미지 아래 가장자리 밖으로 투영되는 Cyclist, 그리고 프레임 1에서 3의 Pedestrian)에 1을 썼습니다. 2D 박스는 KITTI의 손으로 그린 박스와 최대 28px 차이가 났으며, 프레임 1에서 8.2m 떨어진 Pedestrian에서였습니다.
LiDAR 큐보이드를 위한 다른 도구
CVAT 커뮤니티는 MIT 라이선스이며 자체 호스팅하고, 포인트 클라우드에 큐보이드를 그리며, 프레임 사이의 큐보이드 트랙을 보간합니다. CVAT의 3D 내보내기는 Kitti Raw Format 1.0과 Sly Point Cloud Format 1.0이고, CVAT의 KITTI 1.0 형식은 2D 이미지를 다룹니다. CVAT Online의 Solo와 Team 요금제는 사용자당 월 결제 시 월 $33, 연 결제 시 월 $23입니다. Segments.ai는 PCD, PLY, KITTI 바이너리를 포함한 형식의 포인트 클라우드를 읽고, 보간과 함께 포인트 클라우드 시퀀스에 라벨을 달며, Segments.ai Fusion 요금제는 센서 퓨전 인터페이스를 추가합니다. Segments.ai는 모든 요금제를 견적으로 책정하고, Core 요금제의 포인트 클라우드를 500,000점으로 제한하며, 무료 학술 라이선스를 제공합니다. Supervisely에는 무료 커뮤니티 요금제와 월 €199부터의 Pro 요금제가 있습니다. 요금 페이지는 큐보이드 추적과 큐보이드 자동 조정을 Supervisely의 포인트 클라우드 기능으로 나열하며, Supervisely의 앱 카탈로그에는 Import KITTI 3D와 Export to KITTI 3D 앱이 있습니다. Xtreme1은 LF AI & Data Foundation이 호스팅하는 Apache-2.0 도구로, 3D 큐보이드, LiDAR-카메라 퓨전, 프레임 시리즈 추적을 제공합니다. 버전 0.9.3은 2026-09-28에 나왔고, BasicAI는 엔터프라이즈 버전을 제공합니다. 가격과 기능은 2026-10-29에 각 도구의 자체 페이지에서 읽은 것입니다.
이 도구 중 다수는 Xtreme1의 OpenPCDet 통합처럼 모델로 큐보이드를 배치하거나 조정하는데, Potato에는 3D 박스용 모델이 없습니다. Potato는 같은 스캔에 여러 어노테이터를 두고 박스에 대한 일치도를 확인해야 하는 연구와, KITTI 변환을 스크립트가 아니라 도구가 해 주길 원하는 팀에 알맞습니다. 포인트 클라우드 어노테이션 도구 비교가 이 도구들을 Potato와 더 자세히 비교합니다.
LiDAR 라벨링에서 Potato의 한계
- 모델 보조 박스나 추적 없음. 박스는 어노테이터나 가져오기에서 오며, 트랙은 키프레임 사이에서 보간으로만 움직입니다.
- 단일 KITTI 스캔의 3D 가져오기 없음.
-f kitti는 KITTI의 2D 박스를 읽고,-f kitti_tracking은 시퀀스를 읽습니다. - 우연 보정이 없는 일치도.
/admin/iaa는 스킴당 평균 3D IoU 점수 하나를 제공하며, 쌍별이나 어노테이터별 내역은 없습니다. - 투영된 2D 박스. 내보내기의 2D 박스는 3D 박스에서 나오며 KITTI의 손으로 그린 박스보다 넓게 나옵니다.
다음으로 볼 자료
- 3D 포인트 클라우드와 큐보이드, 모든 포인트 클라우드 형식과 옵션
- 보정, 카메라 패널과 Potato가 읽는 보정 형식
- 3D 포인트 클라우드 어노테이션 방법, 회전, 프레임, 상세 수준
- 컴퓨터 비전 형식, 각 가져오기 및 내보내기 형식이 보존하는 것
- 프로젝트 가져오기,
potato import의 다른 형식 - 컴퓨터 비전과 로보틱스와 체화 AI, 이 사이트의 관련 작업
참고문헌
- Geiger, A., Lenz, P., & Urtasun, R. (2012). Are we ready for autonomous driving? The KITTI vision benchmark suite. 2012 IEEE Conference on Computer Vision and Pattern Recognition, 3354-3361. https://doi.org/10.1109/cvpr.2012.6248074
- Geiger, A., Lenz, P., Stiller, C., & Urtasun, R. (2013). Vision meets robotics: The KITTI dataset. The International Journal of Robotics Research, 32(11), 1231-1237. https://doi.org/10.1177/0278364913491297