AIHUB 자율주차용 데이터셋을 보면 1프레임의 이미지 당 1개의 Segmentation Annotation JSON 형식의 Labeled data가 매칭 되어 있다.
이를 YOLOv8-seg 학습 형태로 바꾸기 위한 Python 코드를 작성한다.
import os
import json
from glob import glob
import shutil
CLASS_NAMES = [
'Disabled Icon',' Women Icon', 'Compact Car Icon', 'No Parking Sign', 'Traffic Cone', 'Fire Extinguisher', 'Undefined Object',
'Two-wheeled Vehicle', 'Vehicle', 'Wheelchair', 'Stroller', 'Shopping Cart', 'Human'
]
CLASS_MAP = {name: idx for idx, name in enumerate(CLASS_NAMES)}
IMG_WIDTH = 4032
IMG_HEIGHT = 3040
json_root = "YOUR_JSON_PATH"
output_temp_dir = "YOUR_OUTPUT_PATH"
train_dir = "YOUR_TRAIN_PATH"
val_dir = "YOUR_VAL_PATH"
os.makedirs(output_temp_dir, exist_ok=True)
os.makedirs(train_dir, exist_ok=True)
os.makedirs(val_dir, exist_ok=True)
all_json_files = sorted(glob(os.path.join(json_root, "*.json")))
selected_json_files = all_json_files[::4]
def normalize_point(point):
try:
x = float(point['x']) / IMG_WIDTH
y = float(point['y']) / IMG_HEIGHT
return [x, y]
except (KeyError, TypeError):
return None
def process_polygon(polygon):
flat_coords = []
for point in polygon:
if isinstance(point, dict) and 'x' in point and 'y' in point:
norm = normalize_point(point)
if norm:
flat_coords.extend(norm)
else:
print(f"point 형식 이상: {point}")
return None
return flat_coords if len(flat_coords) >= 6 else None
def convert_json_to_yolo_seg(json_path):
with open(json_path, 'r', encoding='utf-8') as f:
data = json.load(f)
image_name = os.path.splitext(os.path.basename(data['data_key']))[0]
if image_name.startswith("R") or image_name.startswith("L"):
print(f"좌우 카메라 제외: {image_name}")
return
label_lines = []
for obj in data.get("objects", []):
class_name = obj.get("class_name")
if class_name not in CLASS_MAP:
continue
class_id = CLASS_MAP[class_name]
annotations = obj.get("annotation", [])
for polygon_group in annotations:
if not polygon_group:
continue
if isinstance(polygon_group[0], dict):
flat_coords = process_polygon(polygon_group)
if flat_coords:
label_lines.append(f"{class_id} " + " ".join(f"{c:.6f}" for c in flat_coords))
elif isinstance(polygon_group[0], list):
for polygon in polygon_group:
flat_coords = process_polygon(polygon)
if flat_coords:
label_lines.append(f"{class_id} " + " ".join(f"{c:.6f}" for c in flat_coords))
if label_lines:
label_path = os.path.join(output_temp_dir, f"{image_name}.txt")
with open(label_path, 'w', encoding='utf-8') as f:
f.write("\n".join(label_lines))
print(f"{image_name}.txt 저장 완료")
for json_file in selected_json_files:
convert_json_to_yolo_seg(json_file)
print(f"총 {len(selected_json_files)}개의 JSON 파일을 변환 완료")
image_files = sorted([
f for f in os.listdir(output_temp_dir)
])
total = len(image_files)
split_idx = int(total * 0.9)
train_files = image_files[:split_idx]
val_files = image_files[split_idx:]
for f in train_files:
shutil.move(os.path.join(output_temp_dir, f), os.path.join(train_dir, f))
for f in val_files:
shutil.move(os.path.join(output_temp_dir, f), os.path.join(val_dir, f))
os.rmdir(os.path.join(output_temp_dir))
print(f"샘플링된 총 {total}개 중 {len(train_files)}개는 train, {len(val_files)}개는 val로 복사 완료.")
CLASS_NAMES = [
'Disabled Icon',' Women Icon', 'Compact Car Icon', 'No Parking Sign', 'Traffic Cone', 'Fire Extinguisher', 'Undefined Object',
'Two-wheeled Vehicle', 'Vehicle', 'Wheelchair', 'Stroller', 'Shopping Cart', 'Human'
]
CLASS_MAP = {name: idx for idx, name in enumerate(CLASS_NAMES)}
IMG_WIDTH = 4032
IMG_HEIGHT = 3040
json_root = "YOUR_JSON_PATH"
output_temp_dir = "YOUR_OUTPUT_PATH"
train_dir = "YOUR_TRAIN_PATH"
val_dir = "YOUR_VAL_PATH"
os.makedirs(output_temp_dir, exist_ok=True)
os.makedirs(train_dir, exist_ok=True)
os.makedirs(val_dir, exist_ok=True)
all_json_files = sorted(glob(os.path.join(json_root, "*.json")))
selected_json_files = all_json_files[::4]
def normalize_point(point):
try:
x = float(point['x']) / IMG_WIDTH
y = float(point['y']) / IMG_HEIGHT
return [x, y]
except (KeyError, TypeError):
return None
def process_polygon(polygon):
flat_coords = []
for point in polygon:
if isinstance(point, dict) and 'x' in point and 'y' in point:
norm = normalize_point(point)
if norm:
flat_coords.extend(norm)
else:
print(f"point 형식 이상: {point}")
return None
return flat_coords if len(flat_coords) >= 6 else None
하나의 폴리곤은 점들(dict들의 리스트)로 구성
각 점을 정규화하여 [x1, y1, x2, y2, ...] 형식으로 변환
폴리곤 점이 3개 이상(즉 6개 값 이상)일 때만 유효
def convert_json_to_yolo_seg(json_path):
with open(json_path, 'r', encoding='utf-8') as f:
data = json.load(f)
image_name = os.path.splitext(os.path.basename(data['data_key']))[0]
if image_name.startswith("R") or image_name.startswith("L"):
print(f"좌우 카메라 제외: {image_name}")
return
label_lines = []
for obj in data.get("objects", []):
class_name = obj.get("class_name")
if class_name not in CLASS_MAP:
continue
class_id = CLASS_MAP[class_name]
annotations = obj.get("annotation", [])
for polygon_group in annotations:
if not polygon_group:
continue
if isinstance(polygon_group[0], dict):
flat_coords = process_polygon(polygon_group)
if flat_coords:
label_lines.append(f"{class_id} " + " ".join(f"{c:.6f}" for c in flat_coords))
elif isinstance(polygon_group[0], list):
for polygon in polygon_group:
flat_coords = process_polygon(polygon)
if flat_coords:
label_lines.append(f"{class_id} " + " ".join(f"{c:.6f}" for c in flat_coords))
if label_lines:
label_path = os.path.join(output_temp_dir, f"{image_name}.txt")
with open(label_path, 'w', encoding='utf-8') as f:
f.write("\n".join(label_lines))
print(f"{image_name}.txt 저장 완료")
for json_file in selected_json_files:
convert_json_to_yolo_seg(json_file)
print(f"총 {len(selected_json_files)}개의 JSON 파일을 변환 완료")
JSON 파일을 열어
필터링
이미지 이름이 "R" 또는 "L"로 시작하면 좌우 카메라 이미지로 판단하고 제외 (정면 카메라 이미지만 사용하기 때문)
클래스가 CLASS_MAP에 없으면 제외
객체 안에 annotation 필드에 다각형(polygons) 존재 시
이미지에 포함된 객체의 세그멘테이션 정보를 .txt로 임시 디렉토리에 저장
image_files = sorted([
f for f in os.listdir(output_temp_dir)
])
total = len(image_files)
split_idx = int(total * 0.9)
train_files = image_files[:split_idx]
val_files = image_files[split_idx:]
for f in train_files:
shutil.move(os.path.join(output_temp_dir, f), os.path.join(train_dir, f))
for f in val_files:
shutil.move(os.path.join(output_temp_dir, f), os.path.join(val_dir, f))
os.rmdir(os.path.join(output_temp_dir))
print(f"샘플링된 총 {total}개 중 {len(train_files)}개는 train, {len(val_files)}개는 val로 이동 완료.")