본문 바로가기

프로젝트/Data_Analysis_Track_33_FinalProject

FinalProject_05(개별 Action 수행, Modeling)

Modeling 수행

- colab으로 대용량 데이터 파일들을 옮기지도 못해서 local에서 진행

 

패키지 및 라이브러리 불러오기

# 패키지 및 라이브러리 불러오기
from ultralytics import YOLO
from PIL import Image
import cv2
import os
import urllib
import yaml
import json

 

 

디렉토리에 파일 개수 잘 들어갔는지 확인하는 코드

- 현재 사진,라벨링 데이터 각각 짝 맞춰서 2만개씩 다운로드 받았기에 모두 20000이 출력되어야 한다.

# 디렉토리 내의 파일 개수 확인
# folder_path = "Training/원천데이터"      -> 다운로드 받은 사진데이터(jpg)들
# folder_path = "Training/라벨링데이터"    -> 다운로드 받은 라벨링데이터(json)들
# folder_path = "Training/labels"          -> json라벨링데이터들을 txt로 변환한 데이터들
folder_path = "Training/resizing_images" # -> (1920,1080)을 (640,360)으로 resize한 데이터들
file_list = os.listdir(folder_path)
file_count = len(file_list)
print(file_count)

 

 

resize한 결과이미지를 담을 디렉토리 생성

# resize할 이미지를 담을 디렉토리 생성
os.mkdir('Training/resizing_images')

 

 

이미지 resize 코드 (1920,1080) -> (640, 360)

- (640, 640)으로 변경하려 했지만 1920,1080 사진 기준으로 작성된 bbox의 위치가 이상하게 바뀔 수 있어서 비율을 맞춰 (640,360)으로 변경

-> Train할 때 image의 size를 정할 수 있어 resize할 필요가 없다. 코드 실행하지 않아도 됨

# 이미지 resize (1920, 1080) -> (640, 360) 나누기3한 비율만큼 줄이기
def resize_images(input_dir, output_dir, new_size):
    os.makedirs(output_dir, exist_ok=True)

    for filename in os.listdir(input_dir):
        if filename.endswith(".jpg"):
            input_path = os.path.join(input_dir, filename)
            output_path = os.path.join(output_dir, filename)

            # image resizing
            image = Image.open(input_path)
            resized_image = image.resize(new_size, Image.ANTIALIAS)
            resized_image.save(output_path)
            
input_images_dir = 'Training/원천데이터'
output_images_dir = 'Training/resizing_images'
new_size = (640, 360)
resize_images(input_images_dir, output_images_dir, new_size)
image_files = [f for f in os.listdir(output_images_dir) if f.endswith('.jpg')]
print(f'Number of files in the RESIZING directory: image {len(image_files)}개')

 

 

json파일을 bbox및 class정보가 담겨져 있는 txt파일로 변환 코드

- (640,360)으로 고정

-> bbox의 좌표를 정규화된 숫자로 매핑해주기에 크기를 고정할 필요가 없다. 이 코드가 아닌 밑의 txt변환 코드 사용

# json 파일을 txt파일로 변환
# categori_id, bbox(x,y,w,h)정규화 좌표

# bbox 좌표: 좌상단 x, 좌상단 y, bbox의 W, box의 H
def convert_to_yolov8_format(bbox, image_width, image_height):
    x_center = (bbox[0] + bbox[2] / 2) / image_width
    y_center = (bbox[1] + bbox[3] / 2) / image_height
    width = bbox[2] / image_width
    height = bbox[3] / image_height
    return x_center, y_center, width, height
dst_dir = 'C:\\Users\\Playdata\\Desktop\\지자체도로부속시설물 파손데이터\\Training'
dataset_dir = 'C:\\Users\\Playdata\\Desktop\\지자체도로부속시설물 파손데이터\\Training\\라벨링데이터'
# Iterate through all JSON files in the dataset directory
for json_file in os.listdir(dataset_dir):
    if json_file.endswith('.json'):
        json_path = os.path.join(dataset_dir, json_file)
        print(len(json_path))      # print
        with open(json_path, 'r', encoding = 'UTF-8') as f:
            data = json.load(f)
        # Process each annotation in the JSON file
        for annotation in data.get('annotations', []):
            label_path = os.path.join(dst_dir, "labels")
            if not os.path.exists(label_path):
                os.makedirs(label_path)
            image_name = data['images'][0]['file_name'].split('.')[0]
            print(f'image_name: {image_name}')
            image_width = 640 # data['images'][0]['width'] 640으로 고정
            image_height = 360 #data['images'][0]['height'] 360으로 고정
            print(f'image_width: {image_width}, image_height: {image_height}')
            label = annotation.get('category_id', None)
            bbox = annotation.get('bbox', None)
            print(f'label: {label}, bbox: {bbox}')
            if label is not None and bbox is not None:
                x_center, y_center, width, height = convert_to_yolov8_format(bbox=bbox,
                                                                             image_width=image_width,
                                                                             image_height=image_height)
                x_center = format(x_center, '.6f')
                y_center = format(y_center, '.6f')
                width = format(width, '.6f')
                height = format(height, '.6f')
                arr = list(map(str, [label, x_center, y_center, width, height]))
                data_line = ' '.join(arr) + '\n'
                # Save the data to a text file
                txt_filename = os.path.join(label_path, f"{image_name}.txt")
                with open(txt_filename, 'a', encoding='UTF-8') as txt_file:
                    txt_file.write(data_line)

 

 

(1920,1080)을 (640,640)으로 resize하는 코드

- size를 640으로 설정하고 학습하는 reference가 많아 (640,640)으로 resize하는 코드

-> Train할 때 image의 size를 정할 수 있어 resize할 필요가 없다. 코드 실행하지 않아도 됨

# json 파일을 txt파일로 변환
# categori_id, bbox(x,y,w,h)정규화 좌표

# bbox 좌표: 좌상단 x, 좌상단 y, bbox의 W, box의 H
def convert_to_yolov8_format(bbox, image_width, image_height, target_width, target_height):
    # 원본 이미지에서 bbox 좌표를 가져옴
    x, y, w, h = bbox
    
    # bbox 좌표를 (0, 1) 범위로 변환
    x_center = (x + w / 2) / image_width
    y_center = (y + h / 2) / image_height
    width = w / image_width
    height = h / image_height
    # 새로운 크기에 맞게 bbox 좌표를 조정
    x_center *= target_width
    y_center *= target_height
    width *= target_width
    height *= target_height
    return x_center, y_center, width, height
    
dst_dir = 'C:\\Users\\Playdata\\Desktop\\지자체도로부속시설물 파손데이터\\Training'
dataset_dir = 'C:\\Users\\Playdata\\Desktop\\지자체도로부속시설물 파손데이터\\Training\\라벨링데이터'
# Iterate through all JSON files in the dataset directory

image_width, image_height = 1920, 1080
target_width, target_height = 640, 640

for json_file in os.listdir(dataset_dir):
    if json_file.endswith('.json'):
        json_path = os.path.join(dataset_dir, json_file)
        print(len(json_path))      # print
        with open(json_path, 'r', encoding = 'UTF-8') as f:
            data = json.load(f)
        # Process each annotation in the JSON file
        for annotation in data.get('annotations', []):
            label_path = os.path.join(dst_dir, "labels")
            if not os.path.exists(label_path):
                os.makedirs(label_path)
            image_name = data['images'][0]['file_name'].split('.')[0]
            print(f'image_name: {image_name}')
            image_width = 640 # data['images'][0]['width'] 640으로 고정
            image_height = 360 #data['images'][0]['height'] 360으로 고정
            print(f'image_width: {image_width}, image_height: {image_height}')
            label = annotation.get('category_id', None)
            bbox = annotation.get('bbox', None)
            print(f'label: {label}, bbox: {bbox}')
            if label is not None and bbox is not None:
                x_center, y_center, width, height = convert_to_yolov8_format(bbox=bbox,
                                                                             image_width=image_width,
                                                                             image_height=image_height,
                                                                             target_width=target_width, 
                                                                             target_height=target_height
                                                                            )
                x_center = format(x_center, '.6f')
                y_center = format(y_center, '.6f')
                width = format(width, '.6f')
                height = format(height, '.6f')
                arr = list(map(str, [label, x_center, y_center, width, height]))
                data_line = ' '.join(arr) + '\n'
                # Save the data to a text file
                txt_filename = os.path.join(label_path, f"{image_name}.txt")
                with open(txt_filename, 'a', encoding='UTF-8') as txt_file:
                    txt_file.write(data_line)

 

 

json파일의 bbox를 정규화된 txt파일로 변환하는 코드

# bbox 좌표: 좌상단 x, 좌상단 y, bbox의 W, box의 H
def convert_to_yolov8_format(bbox, image_width, image_height):
    x_center = (bbox[0] + bbox[2] / 2) / image_width
    y_center = (bbox[1] + bbox[3] / 2) / image_height
    width = bbox[2] / image_width
    height = bbox[3] / image_height

    return x_center, y_center, width, height

 

dst_dir = 'Training'
dataset_dir = 'Training/라벨링데이터'

# Iterate through all JSON files in the dataset directory
for json_file in os.listdir(dataset_dir):
    if json_file.endswith('.json'):
        json_path = os.path.join(dataset_dir, json_file)
        print(len(json_path))      # print

        with open(json_path, 'r', encoding='UTF-8') as f:
            data = json.load(f)

        # Process each annotation in the JSON file
        for annotation in data.get('annotations', []):
            label_path = os.path.join(dst_dir, "labels")

            if not os.path.exists(label_path):
                os.makedirs(label_path)

            image_name = data['images'][0]['file_name'].split('.')[0]
            print(f'image_name: {image_name}')

            image_width = data['images'][0]['width']
            image_height = data['images'][0]['height']
            print(f'image_width: {image_width}, image_height: {image_height}')

            label = annotation.get('category_id', None)
            bbox = annotation.get('bbox', None)
            print(f'label: {label}, bbox: {bbox}')

            if label is not None and bbox is not None:
                x_center, y_center, width, height = convert_to_yolov8_format(bbox=bbox,
                                                                             image_width=image_width,
                                                                             image_height=image_height
                                                                            )

                x_center = format(x_center, '.6f')
                y_center = format(y_center, '.6f')
                width = format(width, '.6f')
                height = format(height, '.6f')

                arr = list(map(str, [label, x_center, y_center, width, height]))
                data_line = ' '.join(arr) + '\n'

                # Save the data to a text file
                txt_filename = os.path.join(label_path, f"{image_name}.txt")
                with open(txt_filename, 'a', encoding='UTF-8') as txt_file:
                    txt_file.write(data_line)

 

정규화된 txt파일: class번호와 정규화된 bbox좌표가 들어가 있다.

 

 

파이썬으로 yaml파일 작성

# write yaml file with python
data = {
        "train" : 'Training',
        "val" : 'Training',
        # "test" : '/tld_sample/test/', optional
        "names" : {1 : 'PE드럼 정상', 2 : 'PE드럼 파손', 3 : 'PE방호벽 정상', 4 : 'PE방호벽 파손', 5 : 'PE안내봉 정상', 6 : 'PE안내봉 파손',
                   7 : '라바콘 정상', 8 : '라바콘 파손', 9 : '시선유도봉 정상', 10 : '시선유도봉 파손', 11 : '제설함 정상', 12 : '제설함 파손', 
                   13 : 'PE입간판 정상', 14 : 'PE입간판 파손', 15 : 'PE휀스 정상', 16 : 'PE휀스 파손'}}

with open('./customA.yaml', 'w', encoding = 'UTF-8') as f :
    yaml.dump(data, f, allow_unicode=True) # allow_unicode -> 한글 깨지지 않도록

# check written file
with open('./customA.yaml', 'r', encoding = 'UTF-8') as f :
    lines = yaml.safe_load(f)
    print(lines)

 

 

학습

model = YOLO('yolov8n.pt')

model.train(data='./customA.yaml' , epochs=5)

 

-> error 발생

 

-> 0번 클래스를 없는 객체로 생각하여 etc로 설정하여 추가

 

그랬더니 ultralytics디렉토리에 있는 setting.yaml파일에서 경로 설정을 제대로 하라는데 아무리 찾아봐도 경로를 찾지못함