-
Step by step object detection, STEP 1. ๋ฐ์ดํฐ์ ์ค๋นํ๊ธฐComputer Science/๊ฐ๋ฐ 2023. 10. 1. 16:51๋ฐ์ํ
๊ฐ์ฒด ๊ฒ์ถ ํ์ต ๋ชจ๋ธ ๊ตฌํ์ ๋จ๊ณ๋ณ๋ก ์ ๋ฆฌํ๋ ํฌ์คํธ๋ฅผ ์์ฑํด๋ณด๋ ค๊ณ ํ๋ค.
์๊ทผ ์์ํ๊ฒ ์ ๊ฒฝ์ธ๊ฒ ๋ง์์ ์ ๋ฆฌํด๋๋ฉด ๋์ค์ ํ์ฉํ๊ธฐ์ ์ข์ ๊ฒ ๊ฐ๋ค.
๋จผ์ ์ด ๊ณผ์ ์ sgrvinod ๊นํ๋ธ์ 'Deep Tutorials for PyTorch' ํํ ๋ฆฌ์ผ์ ์ฐธ๊ณ ํด์ ์งํ๋์๋ค.
๋ด๊ฐ ์ฌ์ฉํ ๊ฐ์ฒด ๊ฒ์ถ ๋ฐ์ดํฐ์ ์ AI HUB ์ '๊ฑด๊ฐ๊ด๋ฆฌ๋ฅผ ์ํ ์์ ์ด๋ฏธ์ง ๋ฐ์ดํฐ' ์ด๋ค.
์ด ๋ฐ์ดํฐ์ ์๋ ์์๊ณผ ๊ด๋ จ๋ 500์ฌ๊ฐ ์นดํ ๊ณ ๋ฆฌ๊ฐ ์กด์ฌํ๋๋ฐ, ์ปดํจํ ์ ๋ฆฌ์์ค๋ฅผ ๊ณ ๋ คํ์ฌ, ๊ณผ์ผ๊ณผ ์ฑ์ ๋๋ถ๋ฅ ์ค ์ผ๋ถ ์นดํ ๊ณ ๋ฆฌ๋ง์ ์ ์ ํ์ฌ ๊ฐ์ฒด ๊ฒ์ถ์ ์งํํ๊ธฐ๋ก ํ๋ค.
๊ทธ๋ฆฌํ์ฌ ์ ์ ๋ ์นดํ ๊ณ ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ๋ค.
- ๊ณผ์ผ: ๋ง๊ณ (1), ๋ฉ๋ก (2), ๋ธ๊ธฐ(3), ๋ธ๋ฃจ๋ฒ ๋ฆฌ(4), ์ฌ๊ณผ(5), ์๋ชฝ(6), ์๋(7), ์ฒ๋๋ณต์ญ์(8), ์ฒญํฌ๋(9), ์ฒด๋ฆฌ(10)
- ์ฑ์: ๊ตฐ๊ณ ๊ตฌ๋ง(11), ๊ตฐ๋ฐค(12), ๊ณ ์ถ(13), ์น์ปค๋ฆฌ(14), ์ฝ๋ผ๋น(15), ํํ๋ฆฌ์นด(16), ํ ๋งํ (17), ํ๊ณ ๋ฒ์ฏ(18), ๋จํธ๋ฐ(19), ํผ๋ง(20)
STEP 1. ๋ฐ์ดํฐ์ ์ค๋นํ๊ธฐ
1) ๋๋ค ์ํ๋ง
๊ฐ ์นดํ ๊ณ ๋ฆฌ ๋ณ๋ก raw data ๋ฅผ ๋ค์ด๋ฐ์ ๋ณด๋ฉด ํ ์นดํ ๊ณ ๋ฆฌ ์์๋ ์ด๋ฏธ์ง๊ฐ ๋๋ฌด ๋ง๋ค. ๊ทธ๋์ ํ์ต ๋ฐ์ดํฐ๋ก 400์ฅ, validation ๋ฐ์ดํฐ๋ก 50์ฅ, ํ ์คํธ ๋ฐ์ดํฐ๋ก 50์ฅ์ ๋ฝ์์ ํ์ฉํ ๊ฒ์ด๋ค.
์ด๋ฅผ ์ํด ๋๋ค ์ํ๋ง์ 500๊ฐ ๋งํผ ์งํํ์๋ค.
๋ชจ๋ ์นดํ ๊ณ ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์ฑ์ผ๋ก ์ด๋ฏธ์ง์ ๋ผ๋ฒจ์ด ์ ์ฅ๋์ด์๋ค. ์ด๊ณณ (source) ์์ 500์ฅ์ ๋๋ค ์ํ๋งํด์ ๋ณต์ฌํ๊ณ ์ธ๋ถ ํด๋๋ก ๋ถ์ฌ๋ฃ๊ธฐ๋ฅผ ํ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ ์ธ๋ถ์ ๋ธ๋ฃจ๋ฒ ๋ฆฌ๋ผ๋ ํด๋๋ฅผ ๋ง๋ค๊ณ ํ์ ํด๋๋ก images์ labels ๋ฅผ ๋ง๋ ๋ค.
< ๊ธฐ์กด ๋ฐ์ดํฐ์ - ๋ธ๋ฃจ๋ฒ ๋ฆฌ ํด๋ >
๐ฆ [์์ฒ] ์์204_Tra โโ ๋ธ๋ฃจ๋ฒ ๋ฆฌ โโ A220121XX_03592.jpg โโ A220121XX_03593.jpg โโ (์ดํ ์๋ต)
๐ฆ [๋ผ๋ฒจ] ์์204_Tra โโ ๋ธ๋ฃจ๋ฒ ๋ฆฌ โโ A220121XX_03592.json โโ A220121XX_03593.json โโ (์ดํ ์๋ต)
< ์๋ก ๋ง๋ ํด๋ >
๐ฆ ๋ธ๋ฃจ๋ฒ ๋ฆฌ โโ images โโ labels
๋๋ค ์ํ๋งํ์ฌ ๋ณต์ฌ ๋ถ์ฌ๋ฃ๊ธฐ๋ฅผ ํ๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค. (500_random_sampling.ipynb)
source_img = "/Users/jeongin/Desktop/แแ กแผแแ ฅแแ ฎ์ด image" destin_img = "/Users/jeongin/Desktop/แแ กแผแแ ฅแแ ฎแแ ต/images" source_json = "/Users/jeongin/Desktop/แแ กแผแแ ฅแแ ฎแแ ต json" destin_json = "/Users/jeongin/Desktop/แแ กแผแแ ฅแแ ฎแแ ต/labels" file_list = os.listdir(source_img) image_list = [f for f in file_list if f.endswith('.jpg')] # ์ด๋ฏธ์ง ํ์ผ 500๊ฐ ๋๋ค ์ํ๋ง random_images = random.sample(image_list, 500) print("random",random_images) # ์ด๋ฏธ์ง ํ์ผ ๋ณต์ฌ ๋ถ์ฌ๋ฃ๊ธฐ copied_files = [] for image in random_images: copy_this = os.path.join(source_img, image) paste_here = os.path.join(destin_img, image) shutil.copy(copy_this, paste_here) copied_files.append(image) print("copied", copied_files)
json_list = [] for img in copied_files: new = img.replace('.jpg', '.json') json_list.append(new) print(json_list) for json in json_list: copy_from = os.path.join(source_json, json) paste_to = os.path.join(destin_json, json) shutil.copy(copy_from, paste_to)โ
์ด ์์ ์ ์๋ฃํ๊ณ ํด๋๋ฅผ ์ฐํด๋ฆญํด '์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ'๋ฅผ ํ๋ฉด 501๊ฐ์ ํ์ผ์ด ๋ค์ด์๋ค๊ณ ๋จ๋๋ฐ, ์ด ํ๋์ ์ ์ฒด๋ '.DS_Store' ๋ผ๋ ํ์ผ๋ก macOS ์ด์ ์ฒด์ ์์ ์ฌ์ฉ๋๋ ์จ๊ฒจ์ง ์์คํ ํ์ผ์ด๋ค. ์ด ํ์ผ์ ํน์ ํด๋์ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ์ ํด๋ ๋ด๋ถ์ ํ์ผ ๋ฐ ํด๋์ ๋ ์ด์์์ ์ ์ฅํ๋ ๋ฐ ์ฌ์ฉ๋๋ฉฐ ๋ฌด์ํด๋ ๋๋ค.
ํฌ๊ธฐ: 501๊ฐ์ ํญ๋ชฉ์ด ์๋ค๊ณ ๋ฌ๋ค. ์์ ๊ณผ์ ์ ์ ์ ํ 20๊ฐ์ ์นดํ ๊ณ ๋ฆฌ์ ๋ํด ๋ฐ๋ณตํ๋ฉฐ ์ด 500x20=10,000์ฅ์ ์ฌ์ง๊ณผ ๋ผ๋ฒจ ํ์ผ์ด ์ ์ฅ๋๋ค.
๋๋ค์ผ๋ก ๋ฝ์ 500๊ฐ์ ์ฌ์ง๊ณผ ๋ผ๋ฒจ์ค 50, 50 ๊ฐ๋ฅผ ๋ผ์ด๋ด์ test, validation ์ ๋๋ ์ฃผ๋ฉด ๋๋ค.
์ด์ ๋ค์ ์๋ก์ด ํด๋๋ฅผ ๋ง๋ค์ด ์ ์ฒด ์นดํ ๊ณ ๋ฆฌ์ ๋ํด train, validation, test ์ ์ผ๋ก ๋ถ๋ฆฌ์ํฌ ๊ฒ์ด๋ค.
๐ฆ all โโ train (8000) โ โโ images โ โโ labels โโ validation (1000) โ โโ images โ โโ labels โโ test (1000) โโ images โโ labels
2) ์ด๋ฏธ์ง resize & json ํ์ผ ์์ (label, resolution ํค ์ถ๊ฐ)
๋ชจ๋ ์ด๋ฏธ์ง๊ฐ ๊ฐ์ ์ฌ์ด์ฆ๋ฅผ ๊ฐ์ง๋๋ก ๋ฆฌ์ฌ์ด์ง ํด์ค๋ค. ๋ฆฌ์ฌ์ด์ง ํด์๋๋ 300x300 ์ด๋ค.
์๋ image ํ์ผ์ ๊ฐ๊ธฐ ๋ค๋ฅธ ํด์๋๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ์ด์ ๋ํ ์ ๋ณด๋ฅผ resolution ์ผ๋ก ๋ฃ์ด์ค๋ค.
๋ํ label_map ์ผ๋ก ๋งคํ๋ label ์ซ์๋ฅผ ๋ฃ์ด์ค๋ค.
์ฝ๋:
import os import random import shutil import json import cv2 source_dir = "/Users/jeongin/Desktop/all/training/images" destin_dir = "/Users/jeongin/Desktop/all/training/destin_images" def resize_and_update_json(image, json_data, target_size=(300,300)): resized_image = cv2.resize(image, target_size) updated_json = [] for obj in json_data: obj["label"] = 20 # ๋ฐ๊ฟ์ค ๋ถ๋ถ updated_json.append(obj) updated_json = [{'label': obj['label'], **obj} for obj in updated_json] resolution = { 'width': image.shape[1], 'height': image.shape[0] } return resized_image, updated_json, resolution for file in source_dir: # ์ด๋ฏธ์ง ํ์ผ ๋ณต์ฌ source_img = os.path.join(file) destin_img = os.path.join(file) # JSON ํ์ผ ๋ณต์ฌ json_file = os.path.splitext(file)[0] + ".json" source_json = os.path.join(source_dir, "labels", json_file) destin_json = os.path.join(destin_dir, "labels", json_file) # Load the image and JSON data image = cv2.imread(source_img) with open(source_json, 'r') as json_f: json_data = json.load(json_f) # Resize image and adjust bounding boxes resized_image, updated_json, resolution = resize_and_update_json(image, json_data) # Save the resized image and updated JSON data cv2.imwrite(destin_img, resized_image) # Group the updated object annotations and resolution into a single dictionary output_data = { 'objects': updated_json, 'resolution': resolution } with open(destin_json, 'w') as json_f: json.dump(output_data, json_f, indent=2)
์ ๋ฐ์ดํธ๋ json ํ์ผ:
3) ์ ๋์ขํ๋ก ๋ณํํด์ค๋ค.
์๋ json ํ์ผ ์์๋ ์ค์ฌ์ขํ (c_x, c_y)์ ํญ (width), ๋์ด(height) ๊ฐ ์๋์ขํ๋ก ๋ค์ด์๋ค.
์ด๊ฒ์ ์ถ์ถํด์ ์ ๋์ขํ๋ก ๋ฃ์ด์ค ๊ฒ์ด๋ค. ์ ๋์ขํ๋ก ๋ณํํ๋ ์ฝ๋๋ ์๋์ ๊ฐ๋ค.
์ฝ๋:
import os import json from PIL import Image, ImageDraw import torch import torchvision.transforms.functional as FT def convert(json_dir): for filename in os.listdir(json_dir): if filename.endswith(".json"): json_file_path = os.path.join(json_dir, filename) with open(json_file_path, 'r') as json_file: data = json.load(json_file) for item in data["objects"]: w = float(item["W"]) h = float(item["H"]) x, y = map(float, item["Point(x,y)"].split(',')) resized_width = 300 resized_height = 300 x_min = (x - w / 2) * resized_width y_min = (y - h / 2) * resized_height x_max = (x + w / 2) * resized_width y_max = (y + h / 2) * resized_height new_xmin = x_min new_ymin = y_min new_xmax = x_max new_ymax = y_max print(new_xmin, new_ymin, new_xmax, new_ymax) item["boxes"] = [new_xmin, new_ymin, new_xmax, new_ymax] with open(json_file_path, 'w') as updated_json_file: json.dump(data, updated_json_file, indent=4) # ์ ๋ฐ์ดํธ๋ JSON ํ์ผ ๊ฒฝ๋ก ์ถ๋ ฅ print(f"์ ๋ฐ์ดํธ๋ JSON ํ์ผ: {json_file_path}") # JSON ํ์ผ์ด ๋ค์ด ์๋ ๋๋ ํ ๋ฆฌ ๊ฒฝ๋ก json_dir = "/Users/jeongin/Desktop/all/train/labels" # JSON ํ์ผ ๋ณํ ํจ์ ํธ์ถ convert(json_dir)
์ต์ข json ํ์ผ ํํ
ํ๋์ ๊ธ์จ ๋ถ๋ถ์ด ์๋กญ๊ฒ ์ ๋ฐ์ดํธ๋ ๋ถ๋ถ์ด๋ค.
4) train_images.json, train_objects.json, test_images.json, test_objects.json ๋ง๋ค๊ธฐ
ํ์ต์ ํ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํด์์ ํ์ต ํจ์์ ์ ๋ ฅํด์ฃผ๋ ค๋ฉด CustomDataset ํด๋์ค ์ ์์ DataLoader ๊ฐ ํ์ํ๋ค.
CustomDataset ์์ ์ด๋ฏธ์ง์ ๋ ์ด๋ธ์ ๋ถ๋ฌ์์ผ ํ๋ค. ์ด๋ ํ์ํ ๋ ์ด๋ธ์ json ํ์ผ ๋ด๋ถ์์ "label" ๊ฐ๊ณผ "boxes" ๋ฟ์ด๋, ํด๋นํ๋ key๋ค๋ง ๊ฐ์ ธ์์ ์๋ก์ด 'train_objects.json' ํ์ผ์ ๋ฆฌ์คํธ๋ก ์ ์ฅํ ๊ฒ์ด๋ค.
๋ํ, ๋ชจ๋ ์ด๋ฏธ์ง์ ์ ๋ ๊ฒฝ๋ก๋ ์๋ก์ด 'train_images.json' ํ์ผ์ ๋ง๋ค์ด ๋ฆฌ์คํธ๋ก ์ ์ฅํ๋ ค๊ณ ํ๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์ด๋ฏธ์ง์ ๊ฒฝ๋ก์, ๊ทธ ์ด๋ฏธ์ง์ ๋ํ label&boxes ๊ฐ ์์๋๋ก ์ ์ฅ๋์ด ์ธ๋ฑ์ค๋ก ๋ถ๋ฌ์ฌ ์ ์์ ๊ฒ์ด๋ค.
< train_images.json ๊ณผ train_objects.json ๋ง๋ค๊ธฐ >
์ดํ ๋ชจ๋ ์ฝ๋๋ ๋ ํฌ์งํ ๋ฆฌ์ utils.py ๋ด๋ถ์ ์กด์ฌํ๋ ์ฝ๋์ด๋ค.
1) label_map ์ง์
๋จผ์ label map ์ ์ ์ํด์ผ ํ๋ค. 20๊ฐ์ ์นดํ ๊ณ ๋ฆฌ๋ฅผ 1๋ถํฐ 20๊น์ง์ ๋ ์ด๋ธ๋ก ์ง์ ํ ๊ฒ์ด๋ค.
๋ ์ด๋ธ 0์ ์ฌ์ง ์์ ์๋ฌด๋ฐ ๊ฐ์ฒด๊ฐ ์๋ ๊ฒฝ์ฐ๋ก 'background' ํด๋์ค๋ก ์ง์ ํ ๊ฒ์ด๋ค.
food_labels = ('mango', 'melon', 'strawberry', 'blueberry', 'apple', 'grapefruit', 'plum', 'peach', 'grape', 'cherry', 'yam', 'chestnuts', 'pepper', 'chicory', 'Kohlrabi', 'Paprika', 'Tomato', 'Mushroom', 'Pumpkin', 'Pimento') label_map = {k: v+1 for v, k in enumerate(food_labels)} label_map['background'] = 0 print(len(label_map)) rev_label_map = {v: k for k, v in label_map.items()} # inverse mapping distinct_colors = ['#e6194b', '#3cb44b', '#ffe119', '#0082c8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#d2f53c', '#fabebe', '#008080', '#000080', '#aa6e28', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#e6beff', '#808080', '#FFFFFF'] label_color_map = {k: distinct_colors[i] for i, k in enumerate(label_map.keys())} print("label map:",label_map) # 'mango':
21 label_map: { 'mango': 1, 'melon': 2, 'strawberry': 3, 'blueberry': 4, 'apple': 5, 'grapefruit': 6, 'plum': 7, 'peach': 8, 'grape': 9, 'cherry': 10, 'yam': 11, 'chestnuts': 12, 'pepper': 13, 'chicory': 14, 'Kohlrabi': 15, 'Paprika': 16, 'Tomato': 17, 'Mushroom': 18, 'Pumpkin': 19, 'Pimento': 20, 'background': 0 }
2) json ํ์ผ์ label๊ณผ boxes ํค์ ๋ํ value ๊ฐ์ ธ์ค๊ธฐ
def parse_annotation(json_path): with open(json_path, 'r') as json_file: data = json.load(json_file) objects = data.get('objects', []) boxes = [] labels = [] for obj in objects: label = obj.get('label') + 1 box = obj.get('boxes') if label is not None and box is not None: xmin, ymin, xmax, ymax = box boxes.append([xmin, ymin, xmax, ymax]) labels.append(label) return {'labels': labels, 'boxes': boxes}
json ํ์ผ ์์๋ ๋ ์ด๋ธ์ด 0~19 ๊น์ง์ ๋งคํ๋๋ก ๋ผ๋ฒจ๋ง๋์ด ์์ผ๋ train_objects.json ์ ๊ฐ์ ธ์ฌ ๋๋ label์ 1์ ๋ํด์ ๊ฐ์ ธ์์ผ ํ๋ค. boxes๋ xmin, ymin, xmax, ymax ๋ก ๊ทธ๋๋ก ๊ฐ์ ธ์จ๋ค.
์ด์ ๊ฐ์ ธ์จ ๊ฐ๋ค์ ์๋ก์ด json ํ์ผ์ write ํด์ ์ ์ฅํ๋ฉด ๋๋ค. ์ด๋ฏธ์ง์ ๊ฒฝ๋ก๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด์ ์ด๋ฏธ์ง์ ๋ชจ๋ ํ์ผ ์ ๋ชฉ์ด ๋ด๊ฒจ์๋ train.txt ์ custom_path (๋ ํฌ์งํ ๋ฆฌ ์ ๋ ๊ฒฝ๋ก) ๋ฅผ ํ์ฉํ ๊ฒ์ด๋ค.
def create_data_lists(custom_path, output_folder): train_images = list() train_objects = list() n_objects = 0 # Training data for path in [custom_path]: with open(os.path.join(path, 'train.txt')) as f: ids = f.read().splitlines() for id in ids: objects = parse_annotation(os.path.join(path, 'train/labels', id + '.json')) if len(objects['boxes']) == 0: continue n_objects += len(objects['labels']) # Use objects['labels'] here train_objects.append(objects) train_images.append(os.path.join(path, 'train/images', id + '.jpg')) assert len(train_objects) == len(train_images) # Save to file with open(os.path.join(output_folder, 'train_images.json'), 'w') as j: json.dump(train_images, j) with open(os.path.join(output_folder, 'train_objects.json'), 'w') as j: json.dump(train_objects, j) print('\nThere are %d test images containing a total of %d objects. ' 'Files have been saved to %s.' % ( len(train_images), n_objects, os.path.abspath(output_folder))) print("length of test_objects", len(train_objects)) print("length of test_images", len(train_images))
์ด์ ์ด ํจ์๋ฅผ ์คํํ๊ธฐ ์ํด custom_path ์ train๊ณผ test ํด๋๋ฅผ ํฌํจํ๊ณ ์๋ ํ์ฌ ๋ ํฌ์งํ ๋ฆฌ์ ๊ฒฝ๋ก๋ฅผ ๋ฃ์ด์ฃผ๊ณ , output_folder์๋ json ํ์ผ๋ค์ ์ ์ฅํ ๊ฒฝ๋ก๋ฅผ ๋ฃ์ด์ค๋ค.
from utils import create_data_lists if __name__ == '__main__': create_data_lists(custom_path = '/Users/jeongin/PycharmProjects/21class_food_detection', output_folder='/Users/jeongin/PycharmProjects/21class_food_detection/train/json_files')
test ๋ฐ์ดํฐ์ ๋ํด์๋ ๋์ผํ๊ฒ create_data_list ํจ์๋ฅผ ์ ์ฉ์์ผ ์ฃผ๋ฉด ๋๋ค.
'Computer Science > ๊ฐ๋ฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
ln -s ๋ช ๋ น์ด (1) 2023.10.11 ํฐ๋ฏธ๋์์ ๊น ์ปค๋ฐํ๊ธฐ (0) 2023.10.06 Install and Set Up Git and GitHub on macOS (0) 2023.08.14 [Google Colab] ๋๋ง์ ๋ชจ๋ ๋ง๋ค์ด์ import ํ๊ธฐ (0) 2023.07.30