05-02 06:32
Notice
Recent Posts
Recent Comments
관리 메뉴

Scientific Computing & Data Science

[Artificial Intelligence / TensorFlow] TensorFlow Object Detection API를 이용한 다물체 인식하기 Part 2. 본문

Artificial Intelligence/TensorFlow

[Artificial Intelligence / TensorFlow] TensorFlow Object Detection API를 이용한 다물체 인식하기 Part 2.

cinema4dr12 2017. 10. 29. 17:44

Written by Geol Choi | 


지난 포스팅에서 약속드린 바와 같이, TensorFlow의 Object Detection API의 예제 코드를 분석하고 응용 예제에 대한 설명을 드리겠습니다.


아래 코드 설명을 이해하려면 지난 포스팅에 소개드린 내용대로 코드를 우선 실행해 보시기를 권장합니다.


* 본 튜토리얼을 시리즈로 진행되며, 각 링크는 다음과 같습니다:

1. Python 라이브러리 불러오기

아시다시피 다음 코드는 python 라이브러리를 불러오는 코드입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import os
import six.moves.urllib as urllib
import sys
import tarfile
import tensorflow as tf
import zipfile
 
from collections import defaultdict
from io import StringIO
from matplotlib import pyplot as plt
from PIL import Image
 
from utils import label_map_util
from utils import visualization_utils as vis_util
cs

 

이 중 몇개의 라이브러리는 사용되지 않는 것들도 섞여 있기는 합니다. Line 14와 15의 label_map_util과 visualization_utils는 현재 Working Directory로 지정되어 있는 [object_detection] 내의 [utils] 폴더 내의 "label_map_util.py"와 "visualization_utils.py"입니다. 이들은 크게 별 내용은 없고 몇 가지 기능을 편리하게 처리할 수 있도록 하는 Wrapper 기능을 합니다.

2. Pre-trained 데이터 정의

다음 코드는 COCO Detection Challenge(Common Objects in COntext)의 데이터로부터 사전학습(Pre-trained) 되어 있는 TensorFlow 그래프 모델(압축파일 형식)을 다운받기 위한 경로와 다운받은 파일 내 그래프 모델 파일(.pb)과 data 폴더 내 Label 파일(.pbtxt) 경로를 정의합니다.


1
2
3
4
5
6
7
8
9
10
# What model to download.
MODEL_NAME = 'ssd_mobilenet_v1_coco_11_06_2017'
MODEL_FILE = MODEL_NAME + '.tar.gz'
DOWNLOAD_BASE = 'http://download.tensorflow.org/models/object_detection/'
 
# Path to frozen detection graph. This is the actual model that is used for the object detection.
PATH_TO_CKPT = MODEL_NAME + '/frozen_inference_graph.pb'
 
# List of the strings that is used to add correct label for each box.
PATH_TO_LABELS = os.path.join('data''mscoco_label_map.pbtxt')
cs

 

.pb 파일은 Google Protocol Buffer의 Binary 형식 파일로써 학습 시에 구성하였던 그래프 구조가 정의되어 있는 파일입니다.


data/mscoco_label_map.pbtxt.pbtxt 파일 역시 Google Protocol Buffer 파일 형식이나 인간이 읽을 수 있는 텍스트로 되어 있는 Label이 정의되어 있습니다. 텍스트 편집기로 파일을 열면 학습되어 있는 오브젝트 리스트를 확인할 수 있습니다. 참고로 학습된 오브젝트 리스트는 다음과 같습니다:


person
bicycle
car
motorcycle
airplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
couch
potted plant
bed
dining table
toilet
tv
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush


다음 코드는 학습된 오브젝트의 개수, 즉, Class의 개수는 90개로, 식별할 수 있는 사물의 종류가 90개임을 의미합니다.


1
NUM_CLASSES = 90
cs


다음 코드는 정의된 URL로부터 파일(ssd_mobilenet_v1_coco_11_06_2017.tar.gz)을 현재의 Working Directory에 다운받습니다.

 

1
2
opener = urllib.request.URLopener()
opener.retrieve(DOWNLOAD_BASE + MODEL_FILE, MODEL_FILE)
cs

 

다음 코드를 살펴보겠습니다.


1
2
3
4
5
tar_file = tarfile.open(MODEL_FILE)
for file in tar_file.getmembers():
    file_name = os.path.basename(file.name)
    if 'frozen_inference_graph.pb' in file_name:
        tar_file.extract(file, os.getcwd())
cs


위의 코드에서 Line 1은 압축 파일 형식인 tarfile 오브젝트를 통해 MODEL_FILE을 엽니다. tar_file.getmembers()를 통해 tar_file의 멤버를 확인할 수 있습니다:


>>> tar_file.getmembers() [<TarInfo 'ssd_mobilenet_v1_coco_11_06_2017' at 0xcebc750>, <TarInfo 'ssd_mobilenet_v1_coco_11_06_2017/model.ckpt.index' at 0xcebcd90>, <TarInfo 'ssd_mobilenet_v1_coco_11_06_2017/model.ckpt.meta' at 0xcebc818>, <TarInfo 'ssd_mobilenet_v1_coco_11_06_2017/frozen_inference_graph.pb' at 0xcebccc8>, <TarInfo 'ssd_mobilenet_v1_coco_11_06_2017/model.ckpt.data-00000-of-00001' at 0xcebca70>, <TarInfo 'ssd_mobilenet_v1_coco_11_06_2017/graph.pbtxt' at 0xcebcb38>]

Line 2~5에서는 모든 tar_file 멤버 중 'frozen_inference_graph.pb' 파일이 있으면 압축을 해제합니다.


아시다시피, TensorFlow의 계산은 데이터플로우 그래프(Dataflow Graph)로 표현이 되므로 기본적인 그래프가 존재하여야 합니다. 아래 코드에서는 detection_graph 변수에 tf.Graph()로 TensorFlow 그래프 클래스 객체를 넘겨주고 이를 기본 그래프(Default Graph)로 설정합니다(with detection_graph.as_default()).


1
2
3
4
5
6
7
detection_graph = tf.Graph()
with detection_graph.as_default():
    od_graph_def = tf.GraphDef()
    with tf.gfile.GFile(PATH_TO_CKPT, 'rb') as fid:
        serialized_graph = fid.read()
        od_graph_def.ParseFromString(serialized_graph)
        tf.import_graph_def(od_graph_def, name='')
cs


Line 3~7은 Protocol Buffer(pb) 파일을 열고 이 파일에 protobuf 형식으로 정의된 내용을 String으로 Parsing 합니다. Parsing된 String은 그래프에 대한 정보이며 od_graph_def 변수에 저장(데이터량이 어마어마 합니다)되고 현재 그래프에 String으로 정의된 그래프 내용을 불러옵니다.


1
2
3
label_map = label_map_util.load_labelmap(PATH_TO_LABELS)
categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True)
category_index = label_map_util.create_category_index(categories)
cs


위의 코드는 [utils] 폴더 내의 Wrapper 함수들을 Wrapper Module들을 통해 라벨(또는 카테고리 정보 - ID & Name)을 불러옵니다.


그리고 다음 코드는 함수를 정의한 것인데, 함수 이름 그대로 image 데이터를 numpy array 형식으로 변환하는 함수입니다.


1
2
3
def load_image_into_numpy_array(image):
  (im_width, im_height) = image.size
  return np.array(image.getdata()).reshape((im_height, im_width, 3)).astype(np.uint8)
cs


아래 코드는 [test_images] 폴더 내의 'image{}.jpg'.format(i) 형식, 즉, image1.jpgimage2.jpg,... 의 형식을 갖는 불러올 이미지 파일들을 정의합니다. 코드 상에서는 총 4개의 이미지로 정의되어 있는데 필요에 따라 파일형식 및 개수는 얼마든지 변경할 수 있습니다.


1
2
3
# If you want to test the code with your images, just add path to the images to the TEST_IMAGE_PATHS.
PATH_TO_TEST_IMAGES_DIR = 'test_images'
TEST_IMAGE_PATHS = [ os.path.join(PATH_TO_TEST_IMAGES_DIR, 'image{}.jpg'.format(i)) for i in range(14) ]
cs


마지막으로 아래 코드를 살펴보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
with detection_graph.as_default():
  with tf.Session(graph=detection_graph) as sess:
    for image_path in TEST_IMAGE_PATHS:
      image = Image.open(image_path)
 
      # the array based representation of the image will be used later in order to prepare the
      # result image with boxes and labels on it.
      image_np = load_image_into_numpy_array(image)
 
      # Expand dimensions since the model expects images to have shape: [1, None, None, 3]
      image_np_expanded = np.expand_dims(image_np, axis=0)
      image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
 
      # Each box represents a part of the image where a particular object was detected.
      boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
 
      # Each score represent how level of confidence for each of the objects.
      # Score is shown on the result image, together with the class label.
      scores = detection_graph.get_tensor_by_name('detection_scores:0')
      classes = detection_graph.get_tensor_by_name('detection_classes:0')
      num_detections = detection_graph.get_tensor_by_name('num_detections:0')
 
      # Actual detection.
      (boxes, scores, classes, num_detections) = sess.run(
          [boxes, scores, classes, num_detections],
          feed_dict={image_tensor: image_np_expanded})
 
      # Visualization of the results of a detection.
      vis_util.visualize_boxes_and_labels_on_image_array(
          image_np,
          np.squeeze(boxes),
          np.squeeze(classes).astype(np.int32),
          np.squeeze(scores),
          category_index,
          use_normalized_coordinates=True,
          line_thickness=8)
 
      plt.figure(figsize=IMAGE_SIZE)
      plt.imshow(image_np)
cs


Line 2에서는 TensorFlow 세션(Session)을 수립하는데 그래프는 detection_graph를 런칭(Launching)합니다. Line 3에서 TEST_IMAGE_PATHS로 정의하였던 이미지 파일들을 하나씩 불러와서 Line 4에 각각 파일을 엽니다. Line 8에서 load_image_into_numpy_array Wrapper 모듈로부터 오픈한 이미지 데이터를 numpy array로 로딩하고, Line 11에서 그래프 모델에 적합하도록 Array의 Shape을 확장합니다.


Line 12~21은 학습 시 정의했던 이름으로 Tensor를 얻어오며, Line 24~26은 이미 학습된 그래프를 통해 오브젝트를 감지하는데 결과로써 Bounding Box와 Bounding Box 내 오브젝트 스코어 및 클래스, 감지된 오브젝트 개수 등을 얻습니다.


Line 29~36에서는 앞서 얻은 결과를,

visualization_utils.visualize_boxes_and_labels_on_image_array Wrapper Module을 이용하여 현재의 이미지 상에 시각화합니다. 그리고, Line 38~39에서 창을 띄워 이들을 보여줍니다.


이로써 TensorFlow의 Object Detection API의 샘플 코드 설명을 마치도록 하고, 본 샘플 코드를 이용하여 아래의 YouTube 영상과 같이 도로, 동물, 길거리 등에 적용해 보았습니다.

응용 예시

제가  자주 가는 성남 분당의 성남대로 투어의 유튜브 영상을 Object Detection 테스트한 결과입니다. 가로등을 가끔 연(Kite)으로 인식하는군요. 유튜브에 올라온 다른 유사한 영상을 보니 가로등을 연으로 인식하는 경우가 종종 보입니다.



 

다음 동영상은 유튜브에 있는 재미있는 동물 영상입니다. 아직까지는 개와 고양이 구별이 쉽지 않은 듯 해 보이네요. 특히 검은 고양이 뒷모습은 곰(Bear)으로 인식하는 경우가 많았습니다. 하지만 RNN이 아니니 1장의 사진만으로는 사람도 동물의 뒷모습만 보고 구별하기는 쉽지 않을 수도 있을 것 같습니다.



마지막으로 서울의 명동 거리를 활보하는 영상입니다. 영상을 좀 빨리 재생해서 정신이 없을 수도 있습니다. 광고판에 등장하는 사람 사진도 사람으로 인식하네요. 인식할 수 있는 Class의 한계와 밀접한 관계가 있을 것 같습니다.


Object Detection API 관련 논문 및 참고 자료

[1] Ross Girshick, Jeff Donahue, Trevor Darrell, Jitendra Malik (2014) Rich feature hierarchies for accurate object detection and semantic segmentation.

[2] R-CNN for Object Detection:
https://courses.cs.washington.edu/courses/cse590v/14au/cse590v_wk1_rcnn.pdf

[3] R-CNN GitHub Site: https://github.com/rbgirshick/rcnn.

[4] Ross Girshick (2015). Fast R-CNN.

[5] Microsoft Research Blog: Object Detection using Fast R-CNN.
[6] Fast R-CNN GitHub Site: https://github.com/rbgirshick/fast-rcnn.
[7] Shaoqing Ren, Kaiming He, Ross Girshick, Jian Sun (2015) Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks.

[8] Faster R-CNN GitHub Site: https://github.com/rbgirshick/py-faster-rcnn.
[9] Blog: A Brief History of CNNs in Image Segmentation: From R-CNN to Mask R-CNN.
[10] Lear OpenCV Blog: Selective Search for Object Detection.
[11] Koen's Website: Segmentation as Selective Search for Object Recognition.
[12] J.R.R. Uijlings, K.E.A. van de Sande, T. Gevers, and A.W.M. Smeulders (2012) Selective Search for Object Recognition
.
[13] Selective Search GitHub Site: 
https://github.com/AlpacaDB/selectivesearch.
[14] Peng Yuan, Yangxin Zhong, Faster R-CNN with Region Proposal Refinement
.
[15] Quora Website: 
How does the region proposal network (RPN) in Faster R-CNN work?


아래는 R-CNN에서 Faster R-CNN에 이르기까지 이들이 어떻게 동작하는지에 대해 설명한 YouTube 영상입니다.


Comments