본문 바로가기
AI/컴퓨터 비전

Pascal데이터셋 + YOLOv8s를 이용한 디텍션해보기

by 바다의 공간 2025. 3. 25.
더보기

반자동화툴 사용해보기

 

CVAT을 이용한 오브젝트 디텍션을 해보려고 한다.

가장 많이 쓰이는 반자동 어노테이션 TOOL('https://www.cvat.ai/')을 사용하려고 합니다.

CVAT은 커스텀데이터셋을 제작할 수 있고, 
그 부분을 YOLO,COCO등 JOSN형태로 또 내보내줍니다. 
현업에서도 많이 사용하는 부분같더라구요.

starbucks.zip
1.80MB

 

 

더보기

 

 

 

이 이미지를 가지고 데이터어노테이션(주석)을 달아보려고 합니다.

 

어노테이션중에서도 폴리곤을 해보려고한다.  지난번에는 그낭 바운딩박스로 했는데 폴리곤은 처음해보는것같다.

해보니까 생각보다 많이 세심하게 작업해야될것같다.

사용방법은 > 오각형을 누르고 시프트를 누르면 그냥 드래그하면 클릭하지않아도 잘 따라와집니다.

맘에 안들면 esc 누르면 지워지고  재작업 해도됩니다.


Object Detection

Object Detection(객체 탐지)은 이미지나 영상에서 특정 객체의 존재 여부를 확인하고, 해당 객체의 위치를 바운딩 박스(bounding box)로 표시하는 기술입니다. 이는 컴퓨터 비전에서 중요한 분야로, 이미지 내에서 여러 개의 객체를 동시에 탐지하고 분류할 수 있습니다. Object Detection은 주로 딥러닝 기반의 CNN(합성곱 신경망) 모델을 활용하며, 대표적인 알고리즘으로는 R-CNN 계열(Faster R-CNN, Mask R-CNN), YOLO(You Only Look Once), SSD(Single Shot MultiBox Detector) 등이 있습니다. 이러한 기술은 자율 주행, 보안 감시, 의료 영상 분석, 증강 현실 등 다양한 분야에서 활용됩니다.

Object Detection은 방식에 따라 크게 딥러닝 기반 기법 전통적인 기법으로 나눌 수 있으며, 딥러닝 기반 기법은 다시 1단계 탐지(One-Stage)와 2단계 탐지(Two-Stage) 방식으로 분류됩니다.

  1. 전통적인 기법
    • HOG(Histogram of Oriented Gradients) + SVM(Support Vector Machine), Haar Cascade, Selective Search 등의 방법을 사용하여 특징을 추출하고 객체를 탐지하는 방식입니다. 하지만 최근에는 딥러닝 방식이 더 우수하여 잘 사용되지 않습니다.
  2. 딥러닝 기반 기법
    • 2단계 탐지(Two-Stage Detection):
      • Two-Stage Detection은 두 단계로 객체를 찾는 방식입니다. 첫 번째 단계에서는 이미지에서 "어디에 객체가 있을 것 같은지" 후보 영역(Region Proposal)을 찾습니다. 예를 들어, Faster R-CNN에서는 Region Proposal Network(RPN)이 다양한 위치에서 객체가 있을 가능성이 높은 영역을 예측합니다. 두 번째 단계에서는 이 후보 영역들을 하나씩 분석하여 "이것이 어떤 객체인지" 분류하고, 바운딩 박스를 정밀하게 조정합니다. 쉽게 말해, 먼저 전체 이미지를 훑어서 "여기에 뭔가 있다!"라고 판단한 후, 더 자세히 들여다보며 "이건 자동차야!"라고 확정하는 방식입니다. 이렇게 두 번에 걸쳐 탐지하기 때문에 정확도가 높지만 속도는 상대적으로 느릴 수 있습니다.
      • 2스테이지라고하는것은 결국 2번 보는거라고 생각하면된다.

    • 1단계 탐지(One-Stage Detection):
      • One-Stage Detection은 객체를 찾을 때 한 번의 과정만 거치는 방식입니다. 이미지를 입력받으면, 네트워크가 바로 객체의 위치(바운딩 박스)와 종류(클래스)를 동시에 예측합니다. 대표적인 알고리즘으로 YOLO(You Only Look Once)와 SSD(Single Shot MultiBox Detector)가 있습니다. 쉽게 말해, 이미지를 여러 영역으로 나눈 뒤, 각각의 영역에서 "여기에 자동차가 있다!"처럼 바로 객체를 판별하는 방식입니다. 이렇게 한 번에 예측하기 때문에 속도가 빠르지만, 정확도는 Two-Stage Detection보다 다소 낮을 수 있습니다. 따라서 실시간 객체 탐지가 중요한 자율주행, 영상 감시, 드론 탐색 등에서 많이 활용됩니다.

 

영역만 나누어서 찾아서 속도는 매우빠르지만 2stagedetection보다는 정확도는 낮을 수 밖에 없습니다.


YOLO

*가장 많이 쓰임*

 

YOLO(You Only Look Once)는 이미지 전체를 한 번에 처리해 객체의 위치와 종류를 동시에 예측하는 실시간 객체 탐지 알고리즘으로, 빠른 속도와 높은 효율성이 특징입니다. 최신 버전인 YOLO12은 이전 모델들보다 개선된 네트워크 구조와 최적화된 학습 기법을 적용해 정확도와 속도를 크게 향상시켰으며, 특히 작은 객체나 복잡한 배경에서도 강력한 탐지 성능을 보입니다. 이를 통해 다양한 실시간 컴퓨터 비전 응용 분야에서 뛰어난 활용도를 제공하고 있습니다.

 

yolo는 1~4까지계속 오픈소스로 업데이트 했었는데 yolo5부터는 울트라틱스가 1~4,5,8까지 많은 사람들이 쓸 수 있게 만들어놨다 나머지는 파생돼서 만들었다 예를들어 9,10,11이런것도 마찬가지다.

 

가장 잘 다듬어져있는 모델은 5번 혹은 8번을 많이 사용했다.

나도 프로젝트할때 v8을 사용했다.

 

 프로젝트하기에는 좋지만 오픈소스를 사용하려면 상업적으로는 사용이 불가능하므로

금액을 지불하고 하는 회사도 있고, 아니면 다른 모델을 사용하는 회사도 있다. 

 

프로젝트하기에는 욜로가 좋을거같긴하지만 바운딩 디텍션해주는 다른 모델들도 알아봐야할것같다.

 


 

YOLOv8 모델 크기별 분류

  • n (nano): 초경량 모델로 모바일 및 임베디드 장치에서 사용하기 적합합니다.
  • s (small): 속도가 빠르면서도 정확도가 준수하여 실시간 추론이 필요한 경우 사용합니다.
  • m (medium): 균형 잡힌 모델로 중간 정도의 성능과 속도를 제공합니다.
  • l (large): 높은 정확도를 요구하는 애플리케이션에서 사용합니다.
  • x (extra-large): 최대 성능을 발휘하지만, 속도가 상대적으로 느립니다.

YOLOv8 모델 유형별 분류

  • YOLOv8 Detect
    • 가장 일반적인 객체 탐지 모델로, 특정 객체의 위치와 크기를 바운딩 박스로 반환합니다.
    • yolov8n.pt, yolov8s.pt, yolov8m.pt, yolov8l.pt, yolov8x.pt 형태로 제공됩니다.
  • YOLOv8 Segment
    • 객체 탐지뿐만 아니라 픽셀 단위의 분할(Segmentation)까지 수행하는 모델입니다.
    • 바운딩 박스뿐만 아니라 객체 마스크까지 제공합니다.
    • yolov8n-seg.pt, yolov8s-seg.pt 등의 형태로 제공됩니다.
  • YOLOv8 Pose
    • 사람의 관절 위치를 탐지하여 포즈를 예측하는 모델입니다.
    • 예를 들어, 사람의 팔, 다리, 머리 등의 관절을 찾아낼 수 있습니다.
    • yolov8n-pose.pt, yolov8s-pose.pt 등의 형태로 제공됩니다.
  • YOLOv8 Classify
    • 이미지에서 객체를 분류하는 모델입니다.
    • 기존의 CNN 기반 분류 모델(ResNet, EfficientNet 등)과 유사한 역할을 합니다.
    • yolov8n-cls.pt, yolov8s-cls.pt 등의 형태로 제공됩니다.

YOLO 실습환경

PascalVOC 2007 데이터셋

[분류와 객체 검출을 위해 만들어진 데이터셋, 총 20개의 클래스]

Person: person
Animal: bird, cat, cow, dog, horse, sheep >>내가 테스트 할 커스텀 데이터셋
Vehicle: aeroplane, bicycle, boat, bus, car, motorbike, train
Indoor: bottle, chair, dining table, potted plant, sofa, tv/monitor

 

train: 2501장, val: 2510장

test: 4952장


#tran + valid 데이터 합친거
!wget http://pjreddie.com/media/files/VOCtrainval_06-Nov-2007.tar

# test데이터
!wget http://pjreddie.com/media/files/VOCtest_06-Nov-2007.tar

 

 

프로젝트 디렉토리 구조는 아래와 같이 만들겁니다.

 

pascal_datasets
pascal_datasets/trainval
pascal_datasets/test
pascal_datasets/VOC
pascal_datasets/VOC/images
pascal_datasets/VOC/labels
pascal_datasets/VOC/images/train2007
pascal_datasets/VOC/images/val2007
pascal_datasets/VOC/images/test2007
pascal_datasets/VOC/labels/train2007
pascal_datasets/VOC/labels/val2007
pascal_datasets/VOC/labels/test2007

 

pathlib설치

from pathlib import Path

os라는 모듈은 파일경로를 만들어주는 모듈인데, pathlib는 객체지향적으로 다룰 수 있는모듈입니다.

파일 및 디게토리 경로를 좀 더 ./ ../ 이런식으로 직관적이고 기능이 좋은 모델입니다.

경로륻 다룰때 쓰입니다.

 

 

파일정리

이후에 file을 만들어줍니다.

#파일만들기
Path('./pascal_datasets/trainval').mkdir(parents=True, exist_ok=True)
Path('./pascal_datasets/test').mkdir(parents=True, exist_ok=True)

 

 

mkdir을 이용해서 파스칼 데이터파일을 만들어줍니다.

 

여기까지가 데이터 디렉터리구조중 3번째 줄까지 만들어진겁니다. 이제 이중for문을 작성해서 다른 데이터셋도 만들어보려고 합니다.

#나머지 디렉터리 구조만들기
for path1 in ('images', 'labels'):
    for path2 in ('train2007', 'val2007', 'test2007'):
        new_path = root / 'VOC' / path1 / path2
        new_path.mkdir(parents=True, exist_ok=True)

 

그러면

이렇게 파일구조가 나오게 된다. 이후 아래 코드를 입력해서 압축을 풀어야한다.

# 압축 풀기
!tar -xvf VOCtrainval_06-Nov-2007.tar -C ./pascal_datasets/trainval/
!tar -xvf VOCtest_06-Nov-2007.tar -C ./pascal_datasets/test/

 

여기서 어노테시션을 보자면

이런식으로 되어있는데, 여기서 봐야할거는 size인데 밑으로 쭉쭉 내려보면 bndbox라고 바운딩 박스도 그리고 있고 object도 각각 다 들어있는 정보의 xml을볼 수 있다.

 

그렇지만 내가 사용할 모델은 yolo이다. 근데 위의 bndbox는 위에있는 어노테이션 좌표가맞지 않다.

 

그래서 욜로 형태로 바꿔주는 컨버팅을 해주는 모듈이 있다. 그것을 활용해보려고한다.

(욜로의 형태 :

xml에서 (Xmin, Ymin, Xmax, Ymax)을 YOLO 모델에서 사용하기 위해서 포멧 변경이필요합니다.

YOLO형식 : 클래스번호 X의Center좌표, Y의Center좌표 너비 높이))

 

 

 

 

 

 

욜로형태로 컨버팅하는 깃허브 클론해오기

!git clone https://github.com/ssaru/convert2Yolo.git

 

 


이렇게 하면 파일창이  conver2Yolo라는 폴더가 생깁니다..

여기서 하단에 있는 requierments.txt를 실행시켜보면 얘가 필요한 파이썬 모듈을 모두 활용하게 됩니다. 

 

텍스트파일에 뭐 예를들어 Numpy==3.0이런식으로 써있다.

하나하나 설치하기 힘드니까 이럴때는 한번에 깔 수 있다.

 

 

 

 

 

 

%cd convert2Yolo/
%pip install -qr requirements.txt

%cd로 현재 이동경로를 conv2Yolo로 바꿔주고 그 안에있는 requirements를 설치해줍니다.

그러면 위 conver2Yolo에서 필요로하는 파일 모듈들을 모두 설치할 수 있다.

    #datasets : 데이터셋이 voc임을 알려줌

    
    #imt_path : 이미지 파일 경로

    #label : 어노테이션(xml)경로

    #conver_output_path :  yolo형식으로 변환된 라벨 저장경로

    #img_type : 사용할 이미지 파일 확장자

    #manifest_path : 변환 과정에서 사용할 추가적인 정보 파일 저장

    #cls_list_file : voc에서 사용할 클래스 목록이 정의된 파일
!python example.py \
--datasets VOC \
--img_path /content/pascal_datasets/trainval/VOCdevkit/VOC2007/JPEGImages \
--label /content/pascal_datasets/trainval/VOCdevkit/VOC2007/Annotations \
--covert_output_path /content/pascal_datasets/VOC/labels/train2007 \
--img_type '.jpg' \
--manifest_path /content \
--cls_list_file ./voc.names

 

이 안에 주석을 하게된다면 에러가 납니다.

이유는 코랩환경에서 ! 를 사용해 shell명령어를 실행하면 주석인식을 하지않고 코드로인식하기때문에 indentationError가 발생하게 됩니다.

voc.names.txt
0.00MB

 

이후 voc.names.txt를 convert2Yolo파일에 넣습니다.

 

 

욜로 포멧은 아래같이 써주어야합니다.

'''
8 0.587 0.733 0.122 0.341
8: 클래스 ID
0.587 : 중심 X좌표
0.733 : 중심 Y좌표
0.122 : BOX너비
0.341 : 박스 높이
'''
 

객체의 위치를 이미지 크기에 맞게 정규화된 좌표값으로 저장합니다.

 

지금 까지 TRAIN/ VAL로 만 바꾼거니까 TEST도 똑같이 바꿔줍니다.

!python example.py \
--datasets VOC \
--img_path /content/pascal_datasets/test/VOCdevkit/VOC2007/JPEGImages \
--label /content/pascal_datasets/test/VOCdevkit/VOC2007/Annotations \
--convert_output_path /content/pascal_datasets/VOC/labels/test2007 \
--img_type '.jpg' \
--manifest_path /content \
--cls_list_file ./voc.names.txt

 

똑같이 하되, test만 바꾸면 됩니다. 


train, val분할하기

# /content/pascal_datasets/trainval/VOCdevkit/VOC2007/ImageSets/Main/val.txt

voc데이터셋에 trainval안에 vocdevkit안에 voc2007에 Imagesets에 main에 val.txt가있습니다.

왜 있냐면 train과 vla이 합쳐져있기때문에 pascal측에서 val은 이걸써라! 하면서 알려준 txt입니다.

 

### 2-4. train, val 분할
* /content/pascal_datasets/trainval/VOCdevkit/VOC2007/ImageSets/Main/val.txt 파일을 읽어서 
/content/pascal_datasets/VOC/labels/train2007 파일 중 txt 문서에 있는 파일을 
/content/pascal_datasets/VOC/labels/val2007 으로 옮기기

 

import shutil

path = '/content/pascal_datasets/trainval/VOCdevkit/VOC2007/ImageSets/Main/val.txt'

with open(path) as f:
    image_ids = f.read().strip().split()
    for id in image_ids:
        ori_path = '/content/pascal_datasets/VOC/labels/train2007'
        mv_path = '/content/pascal_datasets/VOC/labels/val2007'
        shutil.move(f'{ori_path}/{id}.txt', f'{mv_path}/{id}.txt')

 



2-5.labels에 맞게 images 분할하기* /content/pascal_datasets/trainval/VOCdevkit/VOC2007/JPEGImages 와 /content/pascal_datasets/test/VOCdevkit/VOC2007/JPEGImages 에서 이미지를 가져와 디렉토리에 맞게 저장
import os

path = '/content/pascal_datasets'
for folder, subset in ('trainval', 'train2007'), ('trainval', 'val2007'), ('test', 'test2007'):
    ex_imgs_path = f'{path}/{folder}/VOCdevkit/VOC2007/JPEGImages'
    label_path = f'{path}/VOC/labels/{subset}'
    img_path = f'{path}/VOC/images/{subset}'
    print(subset, ": ", len(os.listdir(label_path)))
    for lbs_list in os.listdir(label_path):
        shutil.move(os.path.join(ex_imgs_path, lbs_list.split('.')[0]+'.jpg'),
                    os.path.join(img_path, lbs_list.split('.')[0]+'.jpg'))

 

>> train2007 : 2501 val2007 : 2510 test2007 : 4952 

이렇게 잘 나뉘어진것을 볼 수 있습니다. 경로를 좀 잘 읽어야될것같습니다.

 

여기까지해서 폴더정리는 모두 완료됐습니다.

폴더정리로 인해서 터미널에서는 경로를 다시 바꿔놔야됩니다.

그래서 %cd /content/로 경로에서 빠져나오게 합니다. (contetn를 바라보게)


YOLO 설치

yolo는 무조건 gpu로만 가능하기때문에 colab으로 진행할 예정입니다.

어노테이션도 할거기때문에 욜로랑 어노테이터를 임포트 해줍니다.

!pip install ultralytics
from ultralytics import YOLO
from ultralytics.utils.plotting import Annotator

 

여기서 이제 yolo를 사용하려면 yaml을 사용해야하는데, yaml이란 뭔지 다시한 번 리마인드하자면 접은글과 같다.

custom_voc.yaml
0.00MB

더보기
데이터 설정파일 입니다.사실 원래는 직접 만들어야하는데 이번건은 그냥 주시면서 설명을 해주셨다.파일보면 주석처리(#)되어있는건 안봐도됩니다.path와 train,val,test,nc,names를 좀 보자면각 경로를 의미하고 nc는 클래스 몇개인지 의미하고 그 밑에는 각 20개가 있습니다.잘 되어있는것으로 확인할 수 있습니다.지금같은경우 내가 설정한 파일경로와 좀 다르기때문에 코랩환경에서 직접 변경해줍니다.원본)


수정본)



 

 

그다음 모델학습을 위해서 모델을 불러와야합니다.

model = YOLO('yolov8s.pt')

#학습결과 저장
results = model.train(
    data = '/content/custom_voc.yaml',
    epochs= 10,
    batch=32,
    imgsz=640,
    device=0, #cpu는 -1
    workers=2, #cpu의 threshold숫자
    patience= 5, #얼리스탑
    name= 'custom_s'
)

-----------------------------------------
```
    data: 데이터셋 설정 파일
    epochs: 학습 에포크 수
    batch: 배치 크기
    imgsz: 입력 이미지 크기
    device: GPU(0), CPU(-1)
    workers: 데이터 로딩에 사용할 CPU 스레드 수
    patience: 조기 종료 설정
    name: 학습 결과 저장 폴더명
```

 

여기서 자꾸 분명 모델을 불러왔는데도 에러가 나서 보니

medel = YOLO('yolov8s.pt)를 해야하는데 'v'를 빼서 자꾸 에러가 났습니다.

다시 오타 고쳐주고 에포크를 돌립니다.

 

모델결과 해석

 

모델 돌아갈떄보면 이런식으로 표가좀 나오는데 이 부분중에 파랑색 부분을 좀 해석해야합니다.

 

객체 탐지 모델의 성능 평가 지표

  • 정밀도(Precision): 객체 탐지 모델이 예측한 객체 중 실제로 정답인 객체의 비율을 의미합니다. 즉, 모델이 "이것이 객체다"라고 판단한 것들 중에서 실제로 맞는 것이 얼마나 많은지를 측정합니다. 수식으로 표현하면 Precision = (TP) / (TP + FP)이며, 여기서 TP(True Positive)는 모델이 올바르게 탐지한 객체 수, FP(False Positive)는 잘못 탐지한 객체 수를 뜻합니다. 정밀도가 높을수록 모델이 잘못된 탐지를 덜 한다는 의미입니다.
  • 재현율(Recall): 실제 존재하는 객체 중에서 모델이 올바르게 탐지한 객체의 비율을 의미합니다. 즉, 놓치지 않고 얼마나 많은 객체를 찾았는지를 평가합니다. 수식으로 표현하면 Recall = (TP) / (TP + FN)이며, 여기서 FN(False Negative)은 모델이 놓친 실제 객체 수를 의미합니다. 재현율이 높을수록 모델이 더 많은 객체를 탐지하지만, 때때로 잘못된 탐지(FP)도 많아질 수 있습니다.
  • AP(Average Precision): 특정 클래스에 대해 Precision-Recall 곡선을 기반으로 평균 정밀도를 계산한 값입니다. AP는 Recall을 0에서 1까지 변화시키면서 Precision을 계산한 후 평균을 내는 방식으로 구합니다. 객체 탐지 모델의 성능을 측정하는 대표적인 방법이며, 보통 특정 IoU 기준에서 계산됩니다(예: AP@IoU=0.5는 IoU가 0.5 이상인 경우에 대해 AP를 구함)
  • IoU(Intersection over Union): 예측된 바운딩 박스와 실제 객체의 바운딩 박스가 얼마나 겹치는지를 나타내는 값입니다. IoU는 두 박스가 겹치는 영역의 면적을 두 박스의 총 합집합 면적으로 나누어 계산합니다. 값이 1에 가까울수록 예측 박스와 실제 박스가 거의 일치한다는 뜻이며, 보통 객체 탐지 성능 평가에서 특정 임계값(예: IoU ≥ 0.5)을 기준으로 모델의 정확도를 평가합니다.
  • mAP(mean Average Precision): 여러 클래스에 대한 AP 값을 평균 낸 값으로, 전체 모델의 객체 탐지 성능을 평가하는 종합적인 지표입니다. 단일 클래스가 아닌 전체 데이터셋에 포함된 모든 클래스의 성능을 반영하며, 다양한 IoU 임곗값(예: 0.5~0.95)에 대해 평균을 내기도 합니다. 높은 mAP 값은 모델이 다양한 클래스의 객체를 정확하게 탐지한다는 것을 의미합니다.
    • mAP@50: IoU(Intersection over Union)가 0.5(50%) 이상이면 정답으로 인정하고 계산한 평균 정밀도입니다. 즉, 모델이 대략적인 위치라도 맞추면 성능이 좋게 나옵니다.
    • mAP@50-95: IoU를 0.5부터 0.95까지 0.05 간격으로 변화시키면서 성능을 평가한 평균 정밀도입니다. 즉, 객체의 위치를 더욱 정확하게 맞춰야 높은 점수를 받기 때문에 더 엄격한 기준입니다.
  • 이 모든것들은 runs파일에 모두 학습되게 됩니다. 이후에 제일 좋은 best파일을 가지고 코드를 돌려보면 됩니다.

#val따로돌려보기
model = YOLO("/content/sample_data/runs/detect/custom_s/weights/best.pt")

# 검증 수행
results = model.val(
    data="custom_voc.yaml",  # 데이터셋 설정
    imgsz=640,               # 이미지 크기
    iou=0.5,                 # IoU 임계값
    batch=32,                # 배치 크기
    device=0,                # GPU 사용
    workers=2,               # 데이터 로드 시 병렬 처리할 워커 수
    half=True,               # FP16 연산 활성화 (속도 향상)
    split="test"             # 테스트 데이터셋을 사용
)

print(results)

이런식으로 해서 확인하고 옆에 폴더에 보면 runs에 또 시각화를 할 수 있었습니다.

 

이제는 아예 데이터셋으로 활용하지 않았던 데이터를 가지고 객체탐지를 돌려보도록 하겠습니다.

model = YOLO("/content/sample_data/runs/detect/custom_s/weights/best.pt")

# 객체 탐지 수행
results = model.predict(
    source="/content/pascal_datasets/custom2007",  # 테스트 이미지 폴더
    imgsz=640,           # 입력 이미지 크기
    conf=0.25,           # 신뢰도(Confidence) 임계값
    device=0,            # GPU 사용 (CPU 사용 시 "cpu")
    save=True,           # 탐지 결과 저장
    save_txt=True,       # 탐지 결과를 txt 형식으로 저장 (YOLO 포맷)
    save_conf=True       # 탐지된 객체의 신뢰도 점수도 저장
)

print(results)

 

 

이렇게 결과가 잘 나오는것을확인할 수 있습니다.

 

내가 직접 cvt에서 라벨링을 한것처럼 너무나도 잘 나오는것을 확인할 수 있었습니다.