4 분 소요

객체 탐지

객체 탐지(Object Detection)는 컴퓨터 비전 기술의 세부 분야중 하나로써 주어진 이미지내 사용자가 관심 있는 객체를 탐지하는 기술입니다.

객체 탐지 모델을 만들기에 앞서, 우선시 되어야 할 과정은 바운딩 박스를 만드는 것 입니다.

바운딩 박스란 (X 최소값, Y 최소값, X 최대값, Y 최대값)으로 표현되는 타겟 위치를 사각형으로 표현한 것입니다.

다운로드.jpeg

YOLO란

You Only Look Once

다운로드.png

YOLO는 한 개의 네트워크에서 탐지를 원하는 물체의 영역(bounding box)와 이름을 표시합니다. 원리는 다음과 같습니다.

  1. 이미지를 입력으로 받습니다.(한 장의 이미지이든 비디오이든 웹캠 스트림이든, 컴퓨터 캡쳐 화면이든 nparray로 치환할 수 있다면 뭐든지 받습니다.)

  2. S x S 크기의 그리드로 이미지를 나눕니다. 각 그리드에서 예측을 한 후 이를 종합해서 bounding box를 구성합니다.

YOLD 사용법

YOLO를 실행 할 딥러닝 프레임워크가 필요하다.

YOLO와 호환되는 가장 많이 사용되는 3가지 프레임워크

  • Darknet : YOLO 개발자가 만든 프레임워크. YOLO를 위해 특별히 제작되었다.

    장점 : 빠르다. GPU또는 CPU와 함께 사용가능

    단점 : 리눅스에서만 호환…

  • Darkflow : Darknet을 텐서플로우에 적용한것

    장점 : 빠르고 GPU 또는 CPU와 함께 사용 가능하고 리눅스, 윈도우, 맥에서 호환

    단점 : 설치 복잡

  • OpenCV : 최소 3.4.2버전 필요

    장점 : openCV외에 설치할 것이 없다

    단점 : CPU에서만 작동하기 때문에 비디오를 실시간으로 처리하는 데 속도가 빠르진 않다

OpenCV DNN YOLO

Tensorflow에서 Yolo를 지원하지 않는다. Yolo의 창시자 사이트에 가서 weight와 conf파일을 받아와야 한다.

알고리즘을 로드합니다. 알고리즘을 실행하기 위해서 3개의 파일이 필요합니다.

  • Weight file : 훈련된 model

  • Cfg file : 구성파일. 알고리즘에 관한 모든 설정이 있다.

  • Name files : 알고리즘이 감지할 수 있는 객체의 이름을 포함한다.

https://pjreddie.com/darknet/yolo/

위의 홈페이지에서 받아옵니다.

참고한 본문에서는 yolov3.weights, yolov3.cfg 파일을 사용했다.

데이터셋 준비

자동차 인식 데이터셋

https://www.kaggle.com/sshikamaru/car-object-detection

에서 데이터셋을 받아옵니다.

import pandas as pd
import numpy as np
import cv2
import matplotlib.pyplot as plt
box = pd.read_csv('/content/drive/MyDrive/cardataset/train_solution_bounding_boxes (1).csv')
box
image xmin ymin xmax ymax
0 vid_4_1000.jpg 281.259045 187.035071 327.727931 223.225547
1 vid_4_10000.jpg 15.163531 187.035071 120.329957 236.430180
2 vid_4_10040.jpg 239.192475 176.764801 361.968162 236.430180
3 vid_4_10020.jpg 496.483358 172.363256 630.020260 231.539575
4 vid_4_10060.jpg 16.630970 186.546010 132.558611 238.386422
... ... ... ... ... ...
554 vid_4_9860.jpg 0.000000 198.321729 49.235251 236.223284
555 vid_4_9880.jpg 329.876184 156.482351 536.664239 250.497895
556 vid_4_9900.jpg 0.000000 168.295823 141.797524 239.176652
557 vid_4_9960.jpg 487.428988 172.233646 616.917699 228.839864
558 vid_4_9980.jpg 221.558631 182.570434 348.585579 238.192196

559 rows × 5 columns

sample = cv2.imread('/content/drive/MyDrive/cardataset/training_images/vid_4_1000.jpg')
sample = cv2.cvtColor(sample, cv2.COLOR_BGR2RGB)
point = box.iloc[0]
pt1 = (int(point['xmin']), int(point['ymax']))
pt2 = (int(point['xmax']), int(point['ymin']))
cv2.rectangle(sample, pt1, pt2, color=(255,0,0), thickness=2)
plt.imshow(sample)
<matplotlib.image.AxesImage at 0x7f95257047d0>

sample = cv2.imread('/content/drive/MyDrive/cardataset/training_images/vid_4_10000.jpg')
sample = cv2.cvtColor(sample, cv2.COLOR_BGR2RGB)
point = box.iloc[1]
pt1 = (int(point['xmin']), int(point['ymax']))
pt2 = (int(point['xmax']), int(point['ymin']))
cv2.rectangle(sample, pt1, pt2, color=(255,0,0), thickness=2)
plt.imshow(sample)
<matplotlib.image.AxesImage at 0x7f95231328d0>

YOLO 구현

# Yolo 로드
net = cv2.dnn.readNet("/content/drive/MyDrive/yolov3.weights", "/content/drive/MyDrive/yolov3.cfg")
classes = []
with open("/content/drive/MyDrive/coco.names", "r") as f:
    classes = [line.strip() for line in f.readlines()]
layer_names = net.getLayerNames()
output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
# 이미지 가져오기
img = cv2.imread('/content/drive/MyDrive/cardataset/training_images/vid_4_10000.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
height, width, channels = img.shape

네트워크에서 이미지를 바로 사용할 수 없기때문에 먼저 이미지를 Blob으로 변환해야 한다.

Blob은 이미지에서 특징을 잡아내고 크기를 조정하는데 사용된다.

YOLO가 허용하는 세가지 크기

  • 320 × 320 : 작고 정확도는 떨어지지 만 속도 빠름

  • 609 × 609 : 정확도는 더 높지만 속도 느림

  • 416 × 416 : 중간

blob = cv2.dnn.blobFromImage(img, 1/256, (416, 416), (0, 0, 0), swapRB=True, crop=False)
net.setInput(blob)

# outs는 출력으로 탐지된 개체에 대한 모든 정보와 위치를 제공한다.
outs = net.forward(output_layers)
# 정보를 화면에 표시
class_ids = []
confidences = []
boxes = []
for out in outs:
    for detection in out:
        scores = detection[5:]
        class_id = np.argmax(scores)
        confidence = scores[class_id]
        if confidence > 0.5:
            # Object detected
            center_x = int(detection[0] * width)
            center_y = int(detection[1] * height)
            w = int(detection[2] * width)
            h = int(detection[3] * height)
            # 좌표
            x = int(center_x - w / 2)
            y = int(center_y - h / 2)
            boxes.append([x, y, w, h])
            confidences.append(float(confidence))
            class_ids.append(class_id)

confidence 값이 1에 가까우면 정확도가 높아지고, 0에 가까우면 정확도가 떨어지지만 검출되는 개체 수가 많아진다.

위의 검출 과정에서 동일한 객체에 생기는 바운딩 박스가 많아지기 때문에 이를 제거하기 위한 코드(Non maximum suppresion)가 필요하다.

indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)

마지막으로 모든 정보를 추출하여 화면에 표시합니다.

  • Box : 감지된 개체를 둘러싼 사각형의 좌표

  • Label : 감지된 물체의 이름

  • Confidence : 0에서 1까지의 탐지에 대한 신뢰도

font = cv2.FONT_HERSHEY_PLAIN
colors = np.random.uniform(0, 255, size=(len(boxes), 3))

for i in indexes.flatten():
    x, y, w, h = boxes[i]
    print(x, y, w, h)
    label = str(classes[class_ids[i]])
    confidence = str(round(confidences[i], 2))
    color = colors[i]
    cv2.rectangle(img, (x, y), ((x+w), (y+h)), color, 2)
    cv2.putText(img, label + " " + confidence, (x, y+20), font, 2, (0, 255, 0), 2)

plt.imshow(img)
22 197 92 32
<matplotlib.image.AxesImage at 0x7efe54c00690>

하나의 함수로 시각화 구현

def predict_yolo(img_path):
  # 이미지 가져오기
  img = cv2.imread(img_path)
  img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  height, width, channels = img.shape

  blob = cv2.dnn.blobFromImage(img, 1/256, (416, 416), (0, 0, 0), swapRB=True, crop=False)
  net.setInput(blob) 
  outs = net.forward(output_layers)

  class_ids = []
  confidences = []
  boxes = []
  for out in outs:
      for detection in out:
          scores = detection[5:]
          class_id = np.argmax(scores)
          confidence = scores[class_id]
          if confidence > 0.5:
              # Object detected
              center_x = int(detection[0] * width)
              center_y = int(detection[1] * height)
              w = int(detection[2] * width)
              h = int(detection[3] * height)
              # 좌표
              x = int(center_x - w / 2)
              y = int(center_y - h / 2)
              boxes.append([x, y, w, h])
              confidences.append(float(confidence))
              class_ids.append(class_id)

  indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)

  font = cv2.FONT_HERSHEY_PLAIN
  colors = np.random.uniform(0, 255, size=(len(boxes), 3))
  if len(indexes) > 0:
    for i in indexes.flatten():
        x, y, w, h = boxes[i]
        print(x, y, w, h)
        label = str(classes[class_ids[i]])
        confidence = str(round(confidences[i], 2))
        color = colors[i]
        cv2.rectangle(img, (x, y), ((x+w), (y+h)), color, 2)
        cv2.putText(img, label + " " + confidence, (x, y+20), font, 2, (0, 255, 0), 2)

    plt.imshow(img)
  
  else:
    print('탐지된 물체가 없습니다.')
import glob
import random

paths = glob.glob('/content/drive/MyDrive/cardataset/testing_images/*.jpg')

img_path = random.choice(paths)

predict_yolo(img_path)
211 197 112 29
2 201 52 23

img_path = random.choice(paths)

predict_yolo(img_path)
412 177 187 59
197 195 38 15

카테고리:

업데이트:

댓글남기기