본문 바로가기

BLOG/딥러닝

faster R-CNN (이론 + 실습)

【4】이미지 분류를 넘어, Object Detection 모델 / R-CNN 이론 + 실습

faster R-CNN (이론 + 실습) 

 

Resnet을 입힌 Detection model(이론 + 실습)

 

텐서플로우 공홈에서 배포하고 있는 Faster R-CNN (inception resnet) 모델이다.

코드를 보면서 어떤 구조인지 이해를 해보자

 

import tensorflow as tf

import tensorflow_hub as hub



import matplotlib.pyplot as plt

import tempfile

from six.moves.urllib.request import urlopen

from six import BytesIO



import numpy as np

from PIL import Image

from PIL import ImageColor

from PIL import ImageDraw

from PIL import ImageFont

from PIL import ImageOps



import time

import os

 

해당 코드에서 사용하는 패키지를 선언하고 진행한다

 

코드에는 여러 함수가 있어 함수를 간략히 설명하겠습니다.

 

def display_image(image):

  fig = plt.figure(figsize=(10, 8))

  plt.grid(False)

  plt.axis('off')

  plt.imshow(image)

 

이미지를 그리는 함수다

 

def draw_bounding_box_on_image(image, ymin, xmin, ymax,

                               xmax, color, font, thickness=4,

                               display_str_list=()):

  # 이미지에 경계상자 그리기

  draw = ImageDraw.Draw(image)

  im_width, im_height = image.size #이미지 원사이즈

  (left, right, top, bottom) = (xmin * im_width, xmax * im_width,

                                ymin * im_height, ymax * im_height)

  draw.line([(left, top), (left, bottom), (right, bottom), (right, top),

             (left, top)],width=thickness, fill=color)



  # 이미지 상단에 글자넣기

  # 만약 박스이미지가 이미지를 초과하면 하단에 글씨를 넣는다

  display_str_heights = [font.getsize(ds)[1] for ds in display_str_list]



  # 각각의 디스플레이마다 상단과 하단의 여백을 0.05로 설정한다.

  total_display_str_height = (1 + 2 * 0.05) * sum(display_str_heights)



  if top > total_display_str_height:

    text_bottom = top

  else:

    text_bottom = bottom + total_display_str_height



  for display_str in display_str_list[::-1]:

    text_width, text_height = font.getsize(display_str)

    margin = np.ceil(0.05 * text_height)

    draw.rectangle([(left, text_bottom - text_height - 2 * margin),

                    (left + text_width, text_bottom)],

                   fill=color)

    draw.text((left + margin, text_bottom - text_height - margin),

              display_str,fill="black",font=font)

    text_bottom -= text_height - 2 * margin

 

이미지에 박스를 그려주는 함수다.

PIL(Python Image Library) 패키지를 사용하여 이미지 그린다고 생각하면 된다.

 

#박스 그리기

def draw_boxes(image, boxes, class_names, scores, max_boxes=10, min_score=0.1):

  colors = list(ImageColor.colormap.values())



  # 폰트 지정

  try:

    font = ImageFont.truetype("/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Regular.ttf",25)

  except IOError:

    print("Font not found, using default font.")

    font = ImageFont.load_default()



  for i in range(min(boxes.shape[0], max_boxes)):

    if scores[i] >= min_score: #최소의 값 이상인 값 찾기

      ymin, xmin, ymax, xmax = tuple(boxes[i]) #박스 좌표값 

      display_str = "{}: {}%".format(class_names[i].decode("ascii"),

                                     int(100 * scores[i]))

      color = colors[hash(class_names[i]) % len(colors)]

      image_pil = Image.fromarray(np.uint8(image)).convert("RGB")

      draw_bounding_box_on_image(

          image_pil,ymin,xmin,

          ymax,xmax,color,

          font,display_str_list=[display_str])

      np.copyto(image, np.array(image_pil))

  return image

 

박스의 좌표를 찾고, 그 좌표를 위에서 선언된 함수를 이용해서 그린다

해당 함수에서는 주로 폰트지정, 가장 높은 값을 가진 라벨과 정확도 변수로 저장, 색상 지정 등 박스를 그리기전 준비한다

 

module_handle = "https://tfhub.dev/google/faster_rcnn/openimages_v4/inception_resnet_v2/1" 

detector = hub.load(module_handle).signatures['default']

 

이번에는 미리 학습된 모델인 inception_resnet_v2 불러온다.

모듈안에는 구조나 가중치가 다 정해져 있기 때문에 학습은 할 필요가 없다

 

구조를 보고 싶으면 detector.summary()를 통해 확인 가능하다

 

def load_img(path):

  img = tf.io.read_file(path)

  img = tf.image.decode_jpeg(img, channels=3)

  return img

 

이미지를 불러오는 함수다

기존에 이미지를 불러올 땐  keras.preprocessing.image에서 이미지를 불러왔지만 이번 코드는 케라스를 사용하지 않기에 텐서플로우 패키지에서 이미지를 불러온다

 

def run_detector(detector, path):

  img = load_img(path)



  converted_img  = tf.image.convert_image_dtype(img, tf.float32)[tf.newaxis, ...]

  start_time = time.time()

  result = detector(converted_img)

  end_time = time.time()



  result = {key:value.numpy() for key,value in result.items()}

  print("이미지에서 찾은 %d개의 객체." % len(result["detection_scores"]))

  print("추론한 시간: ", end_time-start_time)



  image_with_boxes = draw_boxes(

      img.numpy(), result["detection_boxes"],

      result["detection_class_entities"], result["detection_scores"])



  display_image(image_with_boxes)

 

이미지를 넣어 faster R-CNN을 실행을 하는 함수다

실행순서는 이미지 불러오기 -> 이미지 타입변경 -> 모델예측 -> 이미지 박스그리기 ->이미지 출력 순으로 실행된다.

결과를 보면 이미지에 라벨과 박스가 그려져 출력된다.

 

 

 

 


작성자 김강빈 kkb08190819@gmail.com / 이원재 ondslee0808@gmail.com