본문 바로가기

8. OpenCV | CV

8/22(화) IT K-DT(121일차) / 8.Segmentation(YoloV8)

 

8. Segmentaion

 

8-1. Segmentation

영상을 여러 부분으로 나누는 프로세스
Segmentation에 대한 설명 웹페이지: https://www.v7labs.com/blog/panoptic-segmentation-guide

 

Panoptic Segmentation: Definition, Datasets & Tutorial [2023]

Panoptic segmentation is the task of simultaneously segmenting objects and classifying them into categories. Discover different approaches to the task.

www.v7labs.com

 

1) Semantic Segmentation
  이미지의 각 픽셀에 라벨을 할당하여 동일한 카테고리에 속하는 모든 객체를 동일한 방식으로 표시. 

  객체의 인스턴스는 구별하지 않음.
  예) 두 대의 자동차가 있으면 두 자동차는 동일한 색상이나 라벨로 표시함.
  모델) U-Net, SegNet, DeepLab, PSPNet, FCN


2) Instance Segmentation
  영상의 각 픽셀에 라벨을 할당하고 동일한 카테고리의 객체도 구별.
  예) 개별 객체의 위치와 그 객체들 사이의 관계를 알고 싶을 때 사용.
  모델) Mask R-CNN, YOLACT, BlendMask, Detectron2, SOLOv2


3) Panoptic Segmentation
  Segmentation을 결합하여 이미지의 모든 픽셀에 라벨을 할당. 

  물체와 배경 모두에 대한 Segmentation을 수행.
  예) 이미지에서 객체와 배경의 위치, 모양, 관계를 모두 알고 싶을 때 사용.
  모델) Panoptic FPN, UPSNet, SipMask, EfficientPS, BlendMask

 

 

 

8-2. YOLOv8

이미지분류, 객체탐지, 인스턴스 분할(Instance Segmentation) 작업에 사용할 수 있는 최신 YOLO 모델.
YOLOv5 모델을 개발한 Ultralytics에 의해 개발되었다.

 

 

YOLO는 2015년 Joseph Redmond가 처음 출시한 이후 컴퓨터비전 커뮤니티에 의해 성장.
초기 버전(1~4)에서는 YOLO가 Redmond가 작성한 커스텀 딥러닝 프레임워크인 DarkNet에서 유지.
Ultralytics는 YOLOv3 레포를 PyTorch로 따라 작성하여 YOLOv5를 출시함.
유연한 Python 구조 덕분에 YOLOv5는 SOTA 레포가 되었음.
Ultralytics는 2023년 1월에 YOLOv8을 출시하였음.

 


YOLOv8은 아직 발표된 논문이 없어 repository와 모델에 대한 정보를 문서를 통해 확인해야 함.

 

 

YOLOv8은 앵커프리(Anchor-free) 모델.
객체 중심을 직접 예측.

 

 

Anchor box에 대한 정보가 있는 웹사이트: https://blog.roboflow.com/what-is-an-anchor-box/

 

What are Anchor Boxes in Object Detection?

Object detection models use anchor boxes to make bounding box predictions. In this post, we explain anchor boxes and why they are used.

blog.roboflow.com

 


Anchor box는 이전 YOLO 모델에서 굉장히 까다로운 알고리즘이었는데, 이는 대상 벤치마크 박스의 분포를 나타낼 수 있지만 사용자 정의 데이터셋의 분포가 아닐 수 있기 때문임.

Anchor free detection은 박스 예측의 수를 줄여, 

Non-Maximum Suppression(NMS) 라는 복잡한 후처리 단계를 가속화하여 처리

 

 

YOLOv8은 훈련 중 이미지를 증강시키는데, 각 epoch마다 모델은 제공된 이미지의 변형을 시킴


모자이크 증강: 
4개의 이미지를 함께 이어붙여서 모델이 새로운 위치에서 객체를 학습하고, 
부분가림이 있는 상태에서 다른 주변 픽셀에 대해 학습하도록 강제.

모자이크 증강은 전체 학습 루틴을 거치는 동안 성능이 저하될 수 있음.

예) 마지막 10개의 학습 epoch에 대해 모자이크 증강을 off시킬 수 있음.
YOLOv8의 Repository // 속도가 빨라지고 규모가 작아짐.


 

8-3. Indoor 데이터셋 활용 예제

주의) Python 3.7 이상에서만 사용이 가능

 

!pip install ultralytics

import yaml # yaml: dataset의 정보(경로 등)를 config해주는 것을 모아둔 파일
import numpy as np
from ultralytics import YOLO
from PIL import Image, ImageDraw, ImageFont # 이미지, 이미지그리기, 이미지폰트 기능
np.random.seed(2023)

model = YOLO('yolov8n.pt') # yolov8n: yolo v8 모델 종류 중 가장 가벼운 모델. 
# 가장 가벼운 모델을 이용하여 pre-trained하여 객체를 만들어줌

 

 

# 해당 사진을 학습된 모델에 넣음.
results = model('https://ultralytics.com/images/bus.jpg')

 

 

# detect 후 box를 그리는 함수 (붙여넣기)
def draw_bbox(draw, bbox, label, color=(0, 255, 0, 255), confs=None, size=15):
    # font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuMathTeXGyre.ttf", size)
    draw.rectangle(bbox, outline=color, width =3)
    def set_alpha(color, value):
        background = list(color)
        background[3] = value
        return tuple(background)
    background = set_alpha(color, 50)
    draw.rectangle(bbox, outline=color, fill=background, width =3)
    background = set_alpha(color, 150)
    text = f"{label}" + ("" if confs==None else f":{conf:0.4}")
    text_bbox = bbox[0], bbox[1], bbox[0]+len(text)*10, bbox[1]+25
    draw.rectangle(text_bbox, outline=color, fill=background, width =3)
    draw.text((bbox[0]+5, bbox[1]+5), text, (0,0,0))

 

# random하게 choice해서 색깔을 변경해줌
color = []
n_classes = 80
for _ in range(n_classes):
    c = list(np.random.choice(range(256), size=3)) + [255]
    c = tuple(c)
    color.append(c)

 

# image를 전처리
img = Image.open('./bus.jpg')
img = img.resize((640,640)) # YOLO 모델의 사진 사이즈는 640*640
width, height = img.size
draw = ImageDraw.Draw(img, 'RGBA') # RGBA: 투명도까지 적용 가능

 

for result in results:
  result = result.cpu() # cpu를 사용
  xyxys = result.boxes.xyxyn # normalized된 형식으로 YOLO의 x, y좌표를 return해오는 방식.
  confs = result.boxes.conf # confidence score(신뢰값)를 뽑아올 수 있음.
  clss = result.boxes.cls # class값을 뽑아올 수 있음.

  xyxys = xyxys.numpy() # numpy로 변환
  clss = map(int, clss.numpy()) # 정수형의 numpy로 변환
  for xyxy, conf, cls in zip(xyxys, confs, clss): # zip함수를 이용해서 하나씩 순회하여 뽑아냄
    xyxy = [xyxy[0] * width, xyxy[1] * height, xyxy[2] * width, xyxy[3] * height]
    # width와 height를 곱해서 실제 좌표를 뽑아냄.
    draw_bbox(draw, bbox=xyxy, label=cls, color=color[cls], confs=confs, size=15)
  img.show()

 

 

8-4. Kaggle의 Indoor 데이터셋 활용 예제

Kaggle의 Dataset을 이용해서 임의로 5장의 사진을 detection한 후, yaml을 이용해 학습해보기.

Dataset 위치: https://www.kaggle.com/datasets/thepbordin/indoor-object-detection

 

Indoor Objects Detection

Indoor objects dataset for YOLOv5 format

www.kaggle.com

 

import yaml
from ultralytics import YOLO
from PIL import Image, ImageDraw
import numpy as np
from tqdm import tqdm
from glob import glob

 

np.random.seed(2023)
dir_main = './dataset/'
filenames_image = glob(f'{dir_main}/train/images/*.jpg')
filenames_label = [filename.replace('images', 'labels').replace('jpg', 'txt') for filename in filenames_image]

 

cnt = 0
classes = ['door', 'openedDoor', 'cabinetDoor', 'refrigeratorDoor', 'window', 
'chair', 'table', 'cabinet', 'couch', 'pole'] # yaml을 사용하지 않음

 

# random하게 choice해서 색깔을 변경해줌
color = []
n_classes = 10
for _ in range(n_classes):
    c = list(np.random.choice(range(256), size=3)) + [255]
    c = tuple(c)
    color.append(c)

 

# detect 후 box를 그리는 함수 (붙여넣기)
def draw_bbox(draw, bbox, label, color=(0, 255, 0, 255), confs=None, size=15):
    draw.rectangle(bbox, outline=color, width =3)
    def set_alpha(color, value):
        background = list(color)
        background[3] = value
        return tuple(background)
    background = set_alpha(color, 50)
    draw.rectangle(bbox, outline=color, fill=background, width =3)
    background = set_alpha(color, 150)
    text = f"{label}" + ("" if confs==None else f":{conf:0.4}")
    text_bbox = bbox[0], bbox[1], bbox[0]+len(text)*10, bbox[1]+25
    draw.rectangle(text_bbox, outline=color, fill=background, width =3)
    draw.text((bbox[0]+5, bbox[1]+5), text, (0,0,0))

 

# image를 전처리
cnt = 5
for filename_image, filename_label in tqdm(zip(filenames_image, filenames_label)):
    img = Image.open(filename_image)
    img = img.resize((640, 640))
    width, height = img.size
    draw = ImageDraw.Draw(img, 'RGBA')
    with open(filename_label, 'r') as f:
        labels = f.readlines()
        labels = list(map(lambda s: s.strip().split(), labels))
    for label in labels:
        cls = int(label[0])+1
        x, y, w, h = map(float, label[1:])
        x1, x2 = width * (x-w/2), width * (x+w/2)
        y1, y2 = height * (y-h/2), height * (y + h/2)
        x1, y1, x2, y2 = map(int, [x1, y1, x2, y2])
        draw_bbox(draw, bbox=(x1, y1, x2, y2), label=classes[cls], color=color[cls], size=15)
    img.show()
    
    cnt -= 1
    if cnt == 0:
        break

 

# yaml 파일 읽어오는 방법
model = YOLO("yolov8n.pt",'data.yaml')

 

data.yaml파일 내부는 이런식으로 수정 (경로는 절대경로를 사용)

 

# 학습
results = model.train(data='./dataset/data.yaml', epochs=10)
results = model.val() # 학습이 끝난 후 valid모드로 변환