본 글은 하기 블로그의 리뷰 글임.

https://deepbaksuvision.github.io/Modu_ObjectDetection/

 

모두를 위한 Object Detection(Object Detection for All) · GitBook

No results matching ""

deepbaksuvision.github.io

 

 

Object Detection이란?

이미지로부터 객체를 판단하는 기술. 

 

Computer Vision에는 주요 3가지 Task가 있는데

  • Classification
  • Single Classfication & Localization & Detection
  • Multi Object Detection & Localization & Classification

이 있다고 한다.

 

이는 목적에 따라 다른듯. 주어진 이미지로부터 어떤 category의 이미지인지 반드시 찾아야하는 문제가 있을 것이고, 주어진 이미지에서 배경과 객체의 구분 / 그리고 그 객체가 '어떤' 이미지인지를 판단하는 문제가 있을 것이고 / 만일 객체라 한 개가 아닌 여러 개라면 여러개에 대한 구분과 각각에 대한 Classification이 필요할 것이다.

 

-> Recognition과 Object Detection이 있는데

  • Object가 어떤 것인지 구분
  • Recognition보다 더 작은 범위로써 Object의 존재 유무만 판단

의 차이라고 한다.

 

-> Recognition 전에 Detection이 선행되어야 함.

 

전통적으로 사용했던 Object Detection 알고리즘은 Feature Engineering 기법을 통해 특징을 추출하여 특징들의 분포에서 경계를 결정하여 찾는 방법을 주로 사용.

 

전통적인 Feature Extraction 방법 예시 : Haar-like feature, HOG(Histogram of Oriented Gradient), SIFT(Scale Invariant Feature Transform), LBP(Local Binary Pattern), MCT(Modified Census Transform) 등 )

전통적인 Boundary Decision 알고리즘 예시 : SVM(Support Vector Machine), Adaboost와 같은 검출 알고리즘(Classifier)

 

결론적으로 전처리 -> 특징 추출 -> 분류 의 파이프라인을 따름.

 

Datasets for Object Detection

사용할 데이터 셋들

 

PASCAL VOC Dataset

위 페이지에서 어찌저찌 다운을 받았다.

근데 다양한 연도의 데이터가 있었지만 여러 사이트들을 보니 2007이 국룰(?)인 것 같아 07년도의 데이터로 받았다.

tar 파일로 되어있는 것을 압축을 풀면 다음과 같다.

 

각 폴더별 내용물은 다음과 같다고 한다.

  • Annotations : JPEGImages 폴더 속 원본 이미지와 같은 이름들의 xml파일들이 존재합니다. Object Detection을 위한 정답 데이터이 됩니다.
  • ImageSets : 어떤 이미지 그룹을 test, train, trainval, val로 사용할 것인지, 특정 클래스가 어떤 이미지에 있는지 등에 대한 정보들을 포함하고 있는 폴더입니다.
  • JPEGImages : *.jpg확장자를 가진 이미지 파일들이 모여있는 폴더입니다. Object Detection에서 입력 데이터가 됩니다.
  • SegmentationClass : Semantic segmentation을 학습하기 위한 label 이미지
  • SegmentationObject : Instance segmentation을 학습하기 위한 label 이미지

 

해당 코드로 이미지를 불러오라고 했지만 나는 ipynb를 사용할 거기에 image_path에다가 이미지 경로를 넣어준다.

그리고 아마 데이터가 조금씩 다른 듯 하다. 저기선 68번 이미지가 들어와졌지만 나는 68번은 없고 숫자가 조금 띄엄띄엄 있었다.

image_path = sys.argv[1]

image = Image.open(image_path).convert("RGB")

plt.figure(figsize=(25,20))
plt.imshow(image)
plt.show()
plt.close()

 

XML 파일 구조

<annotation>
	<folder>VOC2007</folder>
	<filename>000005.jpg</filename>
	<source>
		<database>The VOC2007 Database</database>
		<annotation>PASCAL VOC2007</annotation>
		<image>flickr</image>
		<flickrid>325991873</flickrid>
	</source>
	<owner>
		<flickrid>archintent louisville</flickrid>
		<name>?</name>
	</owner>
	<size>
		<width>500</width>
		<height>375</height>
		<depth>3</depth>
	</size>
	<segmented>0</segmented>
	<object>
		<name>chair</name>
		<pose>Rear</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>263</xmin>
			<ymin>211</ymin>
			<xmax>324</xmax>
			<ymax>339</ymax>
		</bndbox>
	</object>
	<object>
		<name>chair</name>
		<pose>Unspecified</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>165</xmin>
			<ymin>264</ymin>
			<xmax>253</xmax>
			<ymax>372</ymax>
		</bndbox>
	</object>
	<object>
		<name>chair</name>
		<pose>Unspecified</pose>
		<truncated>1</truncated>
		<difficult>1</difficult>
		<bndbox>
			<xmin>5</xmin>
			<ymin>244</ymin>
			<xmax>67</xmax>
			<ymax>374</ymax>
		</bndbox>
	</object>
	<object>
		<name>chair</name>
		<pose>Unspecified</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>241</xmin>
			<ymin>194</ymin>
			<xmax>295</xmax>
			<ymax>299</ymax>
		</bndbox>
	</object>
	<object>
		<name>chair</name>
		<pose>Unspecified</pose>
		<truncated>1</truncated>
		<difficult>1</difficult>
		<bndbox>
			<xmin>277</xmin>
			<ymin>186</ymin>
			<xmax>312</xmax>
			<ymax>220</ymax>
		</bndbox>
	</object>
</annotation>

Annotation에 있는 파일들은 xml 형식의 파일들인데 이는 JPEG이미지들과 1대1 mapping되는 듯 하다.

여기서 각 이미지들이 어떤 정보를 가지고 있는 지 전달해주는 것 같다.

다음은 xml 파일들이 가지고 있는 정보이다.

 

  • <size> : xml파일과 대응되는 이미지의 width, height, channels 정보에 대한 tag입니다.
    • <width> : xml파일에 대응되는 이미지의 width값
    • <height> : xml파일에 대응되는 이미지의 height값
    • <depth> : xml파일에 대응되는 이미지의 channels값
  • <object> : xml파일과 대응되는 이미지속에 object의 정보에 대한 tag입니다.
    • <name> : 클래스 이름을 의미합니다.
    • <bndbox> : 해당 object의 바운딩상자의 정보에 대한 tag입니다.
      • xmin : object 바운딩상자의 왼쪽상단의 x축 좌표값
      • ymin : object 바운딩상자의 왼쪽상단의 y축 좌표값
      • xmax : object 바운딩상자의 우측하단의 x축 좌표값
      • ymax : object 바운딩상자의 우측하단의 y축 좌표값

바운딩 박스는 다음의 박스를 의미함.

 

xml파일을 load하고 xml package를 이용하여 label을 파싱해보자.

 

xml_path = '~~'
print("XML parsing Start\n")

xml = open(xml_path, "r")
tree = Et.parse(xml)
root = tree.getroot()

size = root.find("size")

width = size.find("width").text
height = size.find("height").text
channels = size.find("depth").text

print("Image properties\nwidth : {}\nheight : {}\nchannels : {}\n".format(width, height, channels))

objects = root.findall("object")
print("Objects Description")
for _object in objects:
    name = _object.find("name").text
    bndbox = _object.find("bndbox")
    xmin = bndbox.find("xmin").text
    ymin = bndbox.find("ymin").text
    xmax = bndbox.find("xmax").text
    ymax = bndbox.find("ymax").text

    print("class : {}\nxmin : {}\nymin : {}\nxmax : {}\nymax : {}\n".format(name, xmin, ymin, xmax, ymax))

print("XML parsing END")

다음과 같이 불러지게 된다.

 

#필요한 거 import
import os
import sys
import matplotlib.pyplot as plt
import xml.etree.ElementTree as Et
from xml.etree.ElementTree import Element, ElementTree

from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw

#Annotations와 JPEGImages의 상위 폴더에 위치를 잡아준다.
dataset_path = '/Users/kbj110/Desktop/workspace/ml_dl/VOCdevkit/VOC2007'

#각각의 폴더 이름을 잡아주
IMAGE_FOLDER = "JPEGImages"
ANNOTATIONS_FOLDER = "Annotations"

#각 폴더에 대한 정보를 받아온다. (하위 파일들 리스트 또한 불러옴)
ann_root, ann_dir, ann_files = next(os.walk(os.path.join(dataset_path, ANNOTATIONS_FOLDER)))
img_root, amg_dir, img_files = next(os.walk(os.path.join(dataset_path, IMAGE_FOLDER)))

#그래서 해당 폴더서 각 파일별로 loop를 돌리며
for xml_file in ann_files:

    # XML파일와 이미지파일은 이름이 같으므로, 확장자만 맞춰서 찾습니다.
    img_name = img_files[img_files.index(".".join([xml_file.split(".")[0], "jpg"]))]
    img_file = os.path.join(img_root, img_name)
    image = Image.open(img_file).convert("RGB")
    draw = ImageDraw.Draw(image)

    #xml을 파싱하고 사이즈를 찾아서 각 width, height, channels 값을 받아온다.
    xml = open(os.path.join(ann_root, xml_file), "r")
    tree = Et.parse(xml)
    root = tree.getroot()

    size = root.find("size")

    width = size.find("width").text
    height = size.find("height").text
    channels = size.find("depth").text

    objects = root.findall("object")

    #그러고 objects들의 정보를 받아 loop를 돌리는데 이는 하나의 객체만을 담고 있지 않을 수 있기 때문인듯
    for _object in objects:
        name = _object.find("name").text
        bndbox = _object.find("bndbox")
        xmin = int(bndbox.find("xmin").text)
        ymin = int(bndbox.find("ymin").text)
        xmax = int(bndbox.find("xmax").text)
        ymax = int(bndbox.find("ymax").text)

        # Box를 그릴 때, 왼쪽 상단 점과, 오른쪽 하단 점의 좌표를 입력으로 주면 됩니다.
        draw.rectangle(((xmin, ymin), (xmax, ymax)), outline="red")
        draw.text((xmin, ymin), name)
    
    #그리고 그린다
    plt.figure(figsize=(25,20))
    plt.imshow(image)
    plt.show()
    plt.close()

주어진 코드를 잘 실행하면 다음과 같은 귀여운 고양이 사진도 찾을 수 있다.

 

convert2Yolo

https://github.com/ssaru/convert2Yolo

 

GitHub - ssaru/convert2Yolo: This project purpose is convert voc annotation xml file to yolo-darknet training file format

This project purpose is convert voc annotation xml file to yolo-darknet training file format - ssaru/convert2Yolo

github.com

 

 

convert2Yolo란? :  각종 datasets들을 YOLO저자가 만든 darknet 프레임워크가 사용하는 label format으로 변경해주는 프로젝트

뒤에 나올 내용에서 모든 Object Detection의 구현체의 Dataloader는 이를 사용하므로 해당 프로젝트가 어떻게 구성되어있고 사용하는지를 숙지해아하 Dataloader 이해 가능.

 

---작성 및 공부중---

+ Recent posts