Home [데브코스] 12주차 - DeepLearning Open Dataset and Dataset label format Convert to Yolo format
Post
Cancel

[데브코스] 12주차 - DeepLearning Open Dataset and Dataset label format Convert to Yolo format


딥러닝을 하기 위해서는 데이터셋에 대한 이해가 필요하다. 자신이 진행하고자 하는 문제에 대해 정의하고, 데이터를 가져오는데, 데이터의 클래스가 어떤지, 그에 대한 비율이 어떤지, 어떤 환경을 가지고 있는지 살펴봐야 한다.

데이터를 가져오는 과정은 다음과 같다.

  1. 데이터 탐색(EDA)
    • 데이터의 클래스 종류와 비율, 어떤 환경을 가지는지 확인
  2. 데이터 가공(전처리)
    • 데이터를 가져온 후에는 데이터 전처리를 진행해야 한다. (data augmentation, removing noise)
  3. 데이터 나누기
    • train/valid/test 로 나누는데, 비율을 잘 나눠야 한다. 예를 들어 특정 dataset에서 환경/클래스 비율 등이 완벽하게 나눠지지 않는다. 그러므로 k-cross validation을 사용하되 StratifiedKFold와 같이 클래스 비율에 맞게 분류하는 것도 중요하다.
  4. 모델 생성
  5. 모델 검증
  6. 대규모 학습
    • 대규모를 학습하는데 모델 생성과 검증에 비해 더 많은 시간이 걸리기 때문이다.



자율주행과 관련된 다양한 데이터를 전체적으로 확인할 수 있는 사이트가 있다.

https://scale.com/open-datasets



DataSets

KITTI dataset

KITTI데이터셋은 2012년에 공개된 데이터로 다소 오래된 데이터셋이다. 자율주행을 위한 데이터는 아니지만, 좋은 gps와 lidar 데이터를 가지고 있다.

Kitti - Setup

카메라와 라이다의 위치들이 각각 다르므로, 이 위치를 일치시켜서 코드를 구현해야 한다. 좌표계는 각자의 디바이스를 기준으로 짜여져 있기 때문이다.

KITTI - Object Detection

2d, 3d object detection에 사용되는 데이터셋을 지원한다. 2D OD의 경우 7481장의 training 데이터를 지원하고, 3D OD의 경우 point cloud 를 함께 지원한다.

또한, bird’s eye view에 대한 데이터도 구성되어 있다.


2D object detection

여기 left,right는 KITTI가 카메라 2대를 사용했으므로 왼쪽 카메라, 오른쪽 카메라를 나누어 제공한다. 또한, lidar데이터, 카메라 calibration에 대한 정보도 제공한다.


데이터를 다운 받은 후 label 데이터를 확인한다. kitti의 label format은 다음과 같다.

Truck 0.00 0 -1.57 599.41 156.40 629.75 189.25 2.85 2.63 12.34 0.47 1.49 69.44 -1.56


  • type: 총 9개의 클래스에 대한 정보이다. tram은 길거리에 있는 기차를 말하고, misc는 구별하기 어려운 애매한 차량들을 지칭하는 것이고, doncares는 너무 멀리 있어서 점으로 보이거나, 잘려서 보이는 차량들에 대한 클래스로 지정해놓은 것이다.
  • truncated : 차량이 이미지에서 벗어난 정도를 나타낸다. 0~1사이의 값으로 표현된다.
  • occluded : 다른 오브젝트에 의해 가려진 정도를 나타낸다. 0은 전체가 다 보이는 것, 1은 일부만, 2는 많이 가려진 정도, 3은 아예 안보이는 것으로 나타낸다.
  • alpha : 객체의 각도를 나타낸다.
  • bbox : left, top, right, bottom에 대한 bounding box 좌표를 나타낸다.



KITTI to Yolo label

kitti데이터를 yolo로 변환하기 위해서는 min_x,min_y,max_x,max_y를 center_x, center_y, width, height 로 변환해야 한다.

  • center_x : (max_x + min_x) / 2
  • center_y : (max_y + min_y) / 2
  • width : max_x - min_x
  • height : max_y - min_y
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import torch
from torch.utils.data.dataset import Dataset
from torch.utils.data.dataloader import DataLoader
from torchvision import transforms
import numpy as np
from glob import glob
import os, json, sys

from PIL import Image
import matplotlib.pyplot as plt
import cv2

# Visualization 1 image
def eda(img : str, labels : list = None) -> None:
    img = cv2.imread(img, cv2.IMREAD_ANYCOLOR)
    if labels is not None:
        for label in labels:
            cx, cy, w, h = label['bbox']
            min_x, min_y, max_x, max_y = \
                        int(cx-w//2), int(cy-h//2), int(cx+w//2), int(cy+h//2)
            cv2.rectangle(img, (min_x, min_y), (max_x, max_y), (255,255,0), 2)
            cv2.putText(img, label['name'], org=(min_x,min_y), 
                            fontFace=cv2.FONT_ITALIC, fontScale=1, color=(0,255,0), thickness=2)
    cv2.imshow("img", img)
    cv2.waitKey(0)

# Visualization 20 image
def eda_subplot(label_list : list) -> None:
    plt.figure(figsize=(10,8)) # width, height
    for i,file in enumerate(label_list):
        plt.subplot(4,5,i+1)
        img_path = file.split('\\')[-1].split('.')[0]
        img = "./kitti/images/train/" + img_path + ".png"
        img = Image.open(img)
        img = np.array(img, np.uint8)
        plt.imshow(img)
    plt.show()

# yes/no visualization
visualization = False

# convert min_x, min_y, max_x, max_y => center_x, center_y, width, height
def xyxy2xywh_np(bbox):
    min_x, min_y, max_x, max_y = np.array(bbox, dtype=np.float32)

    center_x = round((max_x + min_x) / 2,2)
    center_y = round((max_y + min_y) / 2,2)
    bbox_width = round(max_x - min_x,2)
    bbox_height = round(max_y - min_y,2)

    bbox = (center_x, center_y, bbox_width, bbox_height)

    return bbox

# initial channels about grayscale
channels = 1

# convert kitti label to yolo label format
class convert2yolo():
    def __init__(self):
        self.label_dir = "./kitti/labels/"
        self.img_dir = "./kitti/images/"
        self.img_train_dir = self.img_dir + "train/"
        self.img_valid_dir = self.img_dir + "valid/"
        self.output_dir = "./yolo/labels/"

        self.class_names = {
                        'Car' : 0, 
                        'Van' : 1, 
                        'Truck' : 2,
                        'Pedestrian' : 3, 
                        'Person_sitting' : 4, 
                        'Cyclist' : 5, 
                        'Tram' : 6,
                        'Misc' : 7,
                        'DontCare' : 8
                    }

        self.label_dir_list = glob(self.label_dir + "/*")
        os.makedirs(self.output_dir, exist_ok=True)

    # save the txt file for yolo label format to convert from kitti label format
    def save(self):
        for file in self.label_dir_list:
            labels = []
            with open(file, 'r', encoding='UTF-8') as f:
                lines = f.readlines()
                for line in lines:
                    line = line.split(' ')
                    class_id = self.class_names[line[0]]
                    cx,cy,w,h = xyxy2xywh_np(line[4:8])
            f.close()
            
            with open(self.output_dir + file.split("\\")[-1],"w+") as yolo_file:
                yolo_file.write(f"{class_id} {cx} {cy} {w} {h}\n")

            # visualization
            img_path = file.split('\\')[-1].split('.')[0]
            img = self.img_train_dir + img_path + ".png"
            if visualization: 
                eda(img, labels)

        yolo_file.close()


    def __len__(self):
        return len(self.label_dir_list)


if __name__ == "__main__":
    convert = convert2yolo()
    convert.save()



BDD100K

버클리 인공지능 실험 연구실에서 제공한 데이터셋으로 다양한 종류의 데이터들이 구축되어 있다. BDD 데이터셋은 다양한 도시, 다양한 환경에서 생성된 데이터들이다. yolov3에 적용할 때는 100K imagesDetection 2020 labels을 다운받는다.

image는 위와 같이 train/val/test로 되어 있고, label에서는 train/val에 대한 json파일만이 존재한다.

json파일은 JavaScript 언어의 자료형을 텍스트로 표현한 포맷으로 데이터의 이름을 지정해줄 수 있어서 다른 언어에서도 많이 사용되는 포맷이다.

형태는 {}를 통해 블록을 분할하고, 이름 : 값, 이름 : 값 ... 의 형태를 가진다. []는 배열을 의미한다.

위의 형태는 BDD100K label에 대한 포맷으로 파일명이 있어서 label의 name을 읽어오면 그에 대한 image를 불러와 읽는다. labels안에는 Bounding box에 대한 정보들이다.


1
2



Cityscape

Cityscape는 segmentation을 위한 데이터셋이다.

홈페이지


cityscape의 데이터 포맷은 gtFine이 labels를 의미하고, train안에는 도시 이름으로 파일들을 구분하고 있다. leftImg8bit가 이미지 파일 경로를 의미한다.

그리고 1개의 이미지 파일에 대해 4개의 label 데이터가 존재한다. polygons.json은 segmentation을 수행하기 위한 polygone 데이터들을 담고 있다. 나머지 label파일이 png인 이유는 segmentation을 할 때는 label이 image파일로 구성되어야 하기 때문이다. 이 label파일이 맞는지 확인하기 위해서는 이미지 파일에 덮어서 맞는지 확인해봐야 한다.


  • color.png

color.png파일은 객체를 고유한 색상으로 표현한 참고용 이미지이다. 이미지마다 본넷이 존재하고 이를 마스킹하고 있는 것을 확인할 수 있다.


  • instancelds.png

instance를 수행할 때 사용하는 파일로 동일한 클래스에 대해 다른 아이디를 가지는 것을 표현하고 있다. 확인해보면 색이 거의 비슷하지만, 자세히보면 다른 값들을 가지고 있다.


  • labellds.png

semantic segmentation을 수행할 때 사용하는 파일로, 클래스 id에 따라 동일한 색상을 가진다.


segmentation을 조금 쉽게 수행할 수 있게 만들어놓은 모델이 있다. 이는 pytorch를 사용하고 있는 간단한 모델이어서 segmentation에 대해 확인해보기 위해서 https://github.com/chenjun2hao/DDRNet.pytorch 의 깃허브를 사용하면 좋다.

이는 json파일을 사용하고 있는데, 이미지의 height,width를 가지고있고, object에 대한 polygon 값을 가지고 있다. 이 polygon은 객체의 shape에 따라 개수가 다르므로 주의해서 사용해야 한다.


This post is licensed under CC BY 4.0 by the author.

[논문 리뷰] Ultra Fast Structure-aware Deep Lane Detection

[데브코스] 12주차 - DeepLearning Data Labeling and Data Augmentation