1 분 소요


Bounding box normalization

yolo에서 label은 txt파일 형태로 입력된다.
하나의 사진에 여러 label이 있을 수도 있는데 한줄에 하나의 bounding box를 나타낸다.

img

순서대로 class, x중심좌표, y중심좌표, box넓이, box높이를 나타낸다.
class를 제외한 4개의 값은 0~1사의 값으로 정규화 되어 소숫점으로 표시된다.

  • kface 프로젝트에서 사용

이번 딥러닝 프로젝트에서 사용한 데이터에서 사전에 제공되는 좌표정보는 눈, 코, 입, 귀 위치 좌표와 얼굴전체, 눈, 코, 입, 귀의 bounding box 좌표정보를 모두 포함하고 있어 정보를 추출하여 따로 label 파일을 작성했다.
x, y좌표값 또한 box의 중심좌표가 아니라 좌측상단 모서리의 값을 나타내어 계산하는 코드를 작성했다.

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
img_size = (230, 346) # (h, w)
classes=['S001_E01', 'S001_E02', 'S001_E03', 'S002_E01', 'S002_E02', 'S002_E03',
         'S003_E01', 'S003_E02', 'S003_E03', 'S004_E01', 'S004_E02', 'S004_E03',
         'S005_E01', 'S005_E02', 'S005_E03', 'S006_E01', 'S006_E02', 'S006_E03']

txt_paths = sorted(glob.glob('/content/yolov5/kface/*/*.txt'))

for txt_path in tqdm(txt_paths):
    filename = txt_path.split('/')[-1]
    label = txt_path.split('/')[-2]
    num_label = classes.index(label)

    with open(txt_path, 'rt') as f:
        lines = f.readlines()

    x, y, w, h = map(int, lines[7].split('\t'))
    x = (x+w/2)/img_size[1]
    y = (y+h/2)/img_size[0]
    w = w/img_size[1]
    h = h/img_size[0]

    target_path = '/content/yolov5/kface/train/labels/'+label+'_'+filename
    if not os.path.isdir('/content/yolov5/kface/train/labels/'):
        os.makedirs('/content/yolov5/kface/train/labels/')
    with open(target_path, 'wt') as f:
        f.write(f'{num_label} {x} {y} {w} {h}')
  • 이미지 데이터 정리
1
2
3
4
5
6
7
8
9
10
jpg_paths = sorted(glob.glob('/content/yolov5/kface/*/*.jpg'))

for jpg_path in tqdm(jpg_paths):
    filename = jpg_path.split('/')[-1]
    label = jpg_path.split('/')[-2]

    target_path = '/content/yolov5/kface/train/images/'+label+'_'+filename
    if not os.path.isdir('/content/yolov5/kface/train/images/'):
        os.makedirs('/content/yolov5/kface/train/images/')
    shutil.move(jpg_path, target_path)
  • 폴더 정리
1
2
for cls in classes:
    shutil.rmtree('/content/yolov5/kface/'+cls+'/')

데이터 나누기

위의 코드들로 train폴더의 images와 labels폴더에 모든 자료를 넣었다.
sklearn의 train_test_split과 stratify옵션을 이용하여 train과 validation 데이터에 label이 균등하게 나눠지도록 했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
txt_paths = sorted(glob.glob('/content/yolov5/kface/train/labels/*.txt'))
jpg_paths = sorted(glob.glob('/content/yolov5/kface/train/images/*.jpg'))

labels = []
for txt_path in txt_paths:
    with open(txt_path, 'rt') as f:
        lines = f.readlines()
    labels.append(lines[0].split(' ')[0])

# labels = pd.DataFrame(labels)
# df = pd.DataFrame(jpg_paths)
# df = pd.concat([df, labels], axis=1)
# df.columns = [0, 1]
df = pd.DataFrame({0:jpg_paths, 1:labels})
1
2
3
4
5
6
7
8
9
10
11
def split_img_label(data_valid, folder_valid):
    
    os.makedirs(folder_valid+'images/')
    os.makedirs(folder_valid+'labels/')

    valid_ind = list(data_valid.index)

    # Valid folder
    for j in tqdm(range(len(valid_ind))):
        shutil.move(data_valid[valid_ind[j]], folder_valid+'images/'+data_valid[valid_ind[j]].split('/')[-1])
        shutil.move('/'+os.path.join(*data_valid[valid_ind[j]].split('/')[:-2])+'/labels/'+data_valid[valid_ind[j]].split('/')[-1].split('.jpg')[0]+'.txt', folder_valid+'labels/'+data_valid[valid_ind[j]].split('/')[-1].split('.jpg')[0]+'.txt')
1
2
3
4
5
6
7
# split
data_train, data_valid, labels_train, labels_valid = train_test_split(df[0], df[1], test_size=0.2, stratify=df[1], random_state=42)

folder_valid_name = '/content/yolov5/kface/valid/'

# Function split
split_img_label(data_valid, folder_valid_name)

Reference

댓글남기기