[CV] Pascal VOC dataset

박경민·2023년 9월 5일
0

[Computer Vision]

목록 보기
3/25

주요 데이터세트

Object Detection 과 Segmentation 을 위한 데이터는 많지만.. 그 중에서도 유명하다 할 수 있는 것은

  • PASCAL VOC: 20개의 오브젝트 카테고리(사람, 강아지, XML format)
  • MS COCO: 80개의 오브젝트 카테고리(하나의 이미지에 다양한 오브젝트들, 많은 모델들이 MS COCO 를 기반으로 pretrained 됨, json Format)
  • Google Open Images: 600개의 오브젝트 카테고리 (csv Format)

PASCAL VOC 2012

  • Classification/Detection
  • Segmentation
  • Action Classification
  • Person Layout

(Annotation 이란? 바운딩박스의 위치나 Object 이름, 즉 정답을 특정 포맷으로 제공한다.)

PASCAL VOC Dataset 을 보자.

Detection 이라면 JPEGImages - Annotations 와 매핑할 수 있다.

PASCAL VOC 2012 데이터 살펴보기

데이터를 직접 다운로드 받고, 데이터를 살펴보자. (annotation 파일을 파싱하는 것까지 해보자.)

다운받는 경로는 아까 그곳이다! 다운을 받고, 디렉도리를 설정한 다음 그 안에다가 압축파일을 풀어주자.

# pascal voc 2012 데이터를 다운로드 후 /content/data 디렉토리에 압축 해제
# DOWNLOAD시 약 3분정도 시간 소요. 아래 디렉토리가 잘 동작하지 않을 경우 https://web.archive.org/web/20140815141459/http://pascallin.ecs.soton.ac.uk/challenges/VOC/voc2012/VOCtrainval_11-May-2012.tar
!mkdir ./data

!wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar
# 다운받은 압축파일을 /content/data 밑에 풀자. 
!tar -xvf VOCtrainval_11-May-2012.tar -C /content/data

.tar 는 밖에다 잘 다운받아졌고, 이를 /content/data 밑에다가 풀어줬다. 드라이브 마운트를 해놓지 않은 상태이므로 드라이브에는 올라가진 않는다.

content/data/ 밑에 위와 같은 이름의 파일들이 잔뜩있다. 구조를 좀 살펴보면

위와 같은데, 이중 지금 사용해볼 것은 Annotations 와 JPEGImages 이다. 5개 정도 확인해보자.

!ls /content/data/VOCdevkit/VOC2012
!ls /content/data/VOCdevkit/VOC2012/JPEGImages | head -n 5

잘 들어간 것을 코드로도 확인할 수 있다.

✅ JPEGImages 디렉토리에 있는 임의의 이미지 보기

디폴트 디렉토리를 루트 파일로 잘 설정해주고, 원하는 jpg 파일의 주소를 os.path.join 함수로 잘 묶은 다음 cv2.imread 로 읽어온다.

cv2.cvtColor 에는 해당 이미지 주소와 cv2.COLOR_BGR2RGB 를 넣는다.

import cv2
import matplotlib.pyplot as plt
import os
%matplotlib inline

# 코랩 버전은 상대 경로를 사용하지 않습니다. /content 디렉토리를 기준으로 절대 경로를 이용합니다. 
# default_dir 은 /content/data 로 지정하고 os.path.join()으로 상세 파일/디렉토리를 지정합니다. 
default_dir = '/content/data'
img = cv2.imread(os.path.join(default_dir, 'VOCdevkit/VOC2012/JPEGImages/2007_000032.jpg'))
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
print('img shape:', img.shape)

plt.figure(figsize=(8, 8))
plt.imshow(img_rgb)
plt.show()

✅ Annotations 디렉토리에 있는 임의의 annotation 파일 보기

좀 전 이미지 파일 5개를 찍어봤을 때 2007_000032.jpg 이름의 사진이 존재했는데, 해당 Annotation 파일이 존재할까? 어떤 형태일지 !cat 을 통해 보자.

!cat /content/data/VOCdevkit/VOC2012/Annotations/2007_000032.xml

구조는 다음과 같다.

  • annotation 이라 하는 태깅이 루트이고
  • 그 밑에 folder, filename, source, size, object 가 있다. 지금은 위 사진에 물체가 사람2, 비행기2 라 object 4개가 있다. (잘렸지만)
  • size 안에는 width, height, depth 가, object 안에는 name, bndbox 바운딩박스 좌표가 주요하게 들어있다.

✅ Annotation xml 파일에 있는 요소를 파싱하기
xml 파일 위의 태그들을 쉽게 파싱하여 접근할 수 있다. ElementTree 를 이용한다면!(다행이도 이를 불러오기 위한 lxml 은 이미 코랩에 설치되어 있다.)
먼저 주소를 각각 ANNO_DIR 과 IMAGE_DIR 로 주자.

import os
import random

# 코랩 버전 절대경로 수정. 
VOC_ROOT_DIR ="/content/data/VOCdevkit/VOC2012/"
ANNO_DIR = os.path.join(VOC_ROOT_DIR, "Annotations")
IMAGE_DIR = os.path.join(VOC_ROOT_DIR, "JPEGImages")

다음은 그 중에서도 하나의 xml 파일 (아까봤던 비행기2, 사람2 사진) 을 파싱해보자. 주소를 조인하여 인자로 전달하고, ET.parse 메서드를 이용한다! 트리를 반환해준다.

# !pip install lxml
import os
import xml.etree.ElementTree as ET # 이걸 이용해 파싱. 

xml_file = os.path.join(ANNO_DIR, '2007_000032.xml')

# XML 파일을 Parsing 하여 Element 생성
tree = ET.parse(xml_file) # 부모-자식 트리가 생성 (root 밑에 object 들이 있는 구조)
root = tree.getroot() # 루트를 가져올 수 있음 (annotation)

# image 관련 정보는 root의 자식으로 존재
image_name = root.find('filename').text # -> 자식 중 filename 이라는 노드를 찾음.  
full_image_name = os.path.join(IMAGE_DIR, image_name) # 이미지까지 포함한 절대경로 
image_size = root.find('size') # -> 자식 중 size 찾음 
image_width = int(image_size.find('width').text) # size 를 찾고 그 자식노드인 width 를 찾아야 함. 
image_height = int(image_size.find('height').text) # size 를 찾고 그 자식노드인 height 를 찾아야 함. 

# 파일내에 있는 모든 object Element를 찾음.
objects_list = []
for obj in root.findall('object'): # -> 모든 object 를 찾고 반복문 돌림 (지금은 4개)
    # object element의 자식 element에서 bndbox를 찾음. 
    xmlbox = obj.find('bndbox')
    # bndbox element의 자식 element에서 xmin,ymin,xmax,ymax를 찾고 이의 값(text)를 추출 
    x1 = int(xmlbox.find('xmin').text)
    y1 = int(xmlbox.find('ymin').text)
    x2 = int(xmlbox.find('xmax').text)
    y2 = int(xmlbox.find('ymax').text)
    
    bndbox_pos = (x1, y1, x2, y2) # 바운딩박스와
    class_name=obj.find('name').text # 이름을 찾고 
    object_dict={'class_name': class_name, 'bndbox_pos':bndbox_pos} # 딕셔너리를 만들어
    objects_list.append(object_dict) # 리스트에 더해줌. (object 하나에 딕셔너리 하나)

print('full_image_name:', full_image_name,'\n', 'image_size:', (image_width, image_height))

for object in objects_list:
    print(object)
  • xml.etree.ElementTree as ET 라는 걸 가져온다. 이제 ET는 파싱하여 트리를 가져올 수 있게 해줄 것이다.

  • ET.parse 에 인자로 파싱하고 싶은 xml 파일의 주소를 준다.

  • 만들어진 트리에 .getroot 메서드를 쓰면 루트를 가져올 수 있다.

  • 전에 말했듯이 자식 노드를 찾고 값까지 가져올 때 .find('자식노드이름').text 를 쓰면 된다.

  • 좌표는 object 아래에 있다고 했다. .findall 을 하게되면 모든 해당 태깅을 잡게되는데, 모든 오브젝트를 잡으면서 그 오브젝트의 좌표를 가져오자. (리스트에 딕셔너리로 정리해서 더한다.)

✅ Bounding box 시각화
annotation 은 정답이므로 이미 정답인 바운딩박스가 잘 그려져있다. 시각화해보자.

import cv2
import os
import xml.etree.ElementTree as ET

xml_file = os.path.join(ANNO_DIR, '2007_000032.xml')

tree = ET.parse(xml_file)
root = tree.getroot()

image_name = root.find('filename').text
full_image_name = os.path.join(IMAGE_DIR, image_name)

img = cv2.imread(full_image_name)
# opencv의 rectangle()는 인자로 들어온 이미지 배열에 그대로 사각형을 그려주므로 별도의 이미지 배열에 그림 작업 수행. 
draw_img = img.copy()
# OpenCV는 RGB가 아니라 BGR이므로 빨간색은 (0, 0, 255)
green_color=(0, 255, 0)
red_color=(0, 0, 255)
blue_color=(255, 0, 0)
purple_color=(128, 0, 127)

# 파일내에 있는 모든 object Element를 찾음.
objects_list = []
for obj in root.findall('object'):
    xmlbox = obj.find('bndbox')
    
    left = int(xmlbox.find('xmin').text)
    top = int(xmlbox.find('ymin').text)
    right = int(xmlbox.find('xmax').text)
    bottom = int(xmlbox.find('ymax').text)
    
    class_name=obj.find('name').text
    
    # draw_img 배열의 좌상단 우하단 좌표에 녹색으로 box 표시 
    # 네모 그리는 인자 (사진, 좌상단, 우하단, 색, thickness)
    cv2.rectangle(draw_img, (left, top), (right, bottom), color=green_color, thickness=1)
    # draw_img 배열의 좌상단 좌표에 빨간색으로 클래스명 표시
    # text 삽입 인자 (사진, 쓸 텍스트, 텍스트 위치, 폰트.. )
    cv2.putText(draw_img, class_name, (left, top - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.4, purple_color, thickness=1)

img_rgb = cv2.cvtColor(draw_img, cv2.COLOR_BGR2RGB)
plt.figure(figsize=(10, 10))
plt.imshow(img_rgb)

cv.rectangle 은 네모그리는 인자이고, cv.putText 는 텍스트 삽입인자이다. 시험삼아 블루컬러와 퍼플컬러 (BGR로)를 만들고 인자로 주니까 진짜 보라색으로 떴다 ㅎㅎ.
업로드중..

profile
Mathematics, Algorithm, and IDEA for AI research🦖

0개의 댓글