본문 바로가기

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

YOLO (이론 + 실습)

YOLO는 현재 v3 모델까지 나온 상황이며  v3 코드를 다루겠다

 

!wget https://pjreddie.com/media/files/yolov3.weights

YOLOv3를 사용하기에 앞서 가중치를 받아야 한다

 

import os

import scipy.io

import scipy.misc

import numpy as np

import pandas as pd

import PIL

import struct

import cv2

from numpy import expand_dims

import tensorflow as tf

from skimage.transform import resize

from tensorflow.keras import backend as K

from tensorflow.keras.layers import Input, Lambda, Conv2D, BatchNormalization, LeakyReLU, ZeroPadding2D, UpSampling2D

from tensorflow.keras.models import load_model, Model

from tensorflow.keras.layers import add, concatenate

from tensorflow.keras.preprocessing.image import load_img

from tensorflow.keras.preprocessing.image import img_to_array

import matplotlib.pyplot as plt

from matplotlib.pyplot import imshow

from matplotlib.patches import Rectangle



%matplotlib inline



PATH = 'Image/'

 

모델을 사용하기에 패키지를 선언한다.

 

def _conv_block(inp, convs, skip=True):

    x = inp #입력레이어

    count = 0 

    

    for conv in convs:

        if count == (len(convs) - 2) and skip:

            skip_connection = x

        count += 1

        

        if conv['stride'] > 1: x = ZeroPadding2D(((1,0),(1,0)))(x) # stride가 2이상이면 padding

        x = Conv2D(conv['filter'],  #필터사이즈

                   conv['kernel'],  #커널사이즈

                   strides=conv['stride'],  #스트라이드

                   padding='valid' if conv['stride'] > 1 else 'same',  #패딩을 1이면 valid 2이상 부터 sasme

                   name='conv_' + str(conv['layer_idx']), #레이어 이름 설정

                   use_bias=False if conv['bnorm'] else True)(x) #use_bias 설정 

        if conv['bnorm']: x = BatchNormalization(epsilon=0.001, name='bnorm_' + str(conv['layer_idx']))(x) #배치노말라이제이션 설정

        if conv['leaky']: x = LeakyReLU(alpha=0.1, name='leaky_' + str(conv['layer_idx']))(x) #리키렐루 설정



    return add([skip_connection, x]) if skip else x

 

YOLO v3는 darknet 구조를 따르기 때문에 100개가 넘는 층으로 되있다

그렇기에  일일이 선언하기에는 코드가 길어지고 많은 시간이 필요로 하기 때문에 모델 생성을 함수로 선언하여 진행한다.

 

def make_yolov3_model():

    input_image = Input(shape=(None, None, 3)) # 이미지 크기가 각각 다르므로 None, None으로 설정



    # Layer  0 => 4

    x = _conv_block(input_image, [{'filter': 32, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 0},

                                  {'filter': 64, 'kernel': 3, 'stride': 2, 'bnorm': True, 'leaky': True, 'layer_idx': 1},

                                  {'filter': 32, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 2},

                                  {'filter': 64, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 3}])



    # Layer  5 => 8

    x = _conv_block(x, [{'filter': 128, 'kernel': 3, 'stride': 2, 'bnorm': True, 'leaky': True, 'layer_idx': 5},

                        {'filter':  64, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 6},

                        {'filter': 128, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 7}])



    # Layer  9 => 11

    x = _conv_block(x, [{'filter':  64, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 9},

                        {'filter': 128, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 10}])



    # Layer 12 => 15

    x = _conv_block(x, [{'filter': 256, 'kernel': 3, 'stride': 2, 'bnorm': True, 'leaky': True, 'layer_idx': 12},

                        {'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 13},

                        {'filter': 256, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 14}])



    # Layer 16 => 36

    for i in range(7):

        x = _conv_block(x, [{'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 16+i*3},

                            {'filter': 256, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 17+i*3}])

        

    skip_36 = x

        

    # Layer 37 => 40

    x = _conv_block(x, [{'filter': 512, 'kernel': 3, 'stride': 2, 'bnorm': True, 'leaky': True, 'layer_idx': 37},

                        {'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 38},

                        {'filter': 512, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 39}])



    # Layer 41 => 61

    for i in range(7):

        x = _conv_block(x, [{'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 41+i*3},

                            {'filter': 512, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 42+i*3}])

        

    skip_61 = x

        

    # Layer 62 => 65

    x = _conv_block(x, [{'filter': 1024, 'kernel': 3, 'stride': 2, 'bnorm': True, 'leaky': True, 'layer_idx': 62},

                        {'filter':  512, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 63},

                        {'filter': 1024, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 64}])



    # Layer 66 => 74

    for i in range(3):

        x = _conv_block(x, [{'filter':  512, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 66+i*3},

                            {'filter': 1024, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 67+i*3}])

        

    # Layer 75 => 79

    x = _conv_block(x, [{'filter':  512, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 75},

                        {'filter': 1024, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 76},

                        {'filter':  512, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 77},

                        {'filter': 1024, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 78},

                        {'filter':  512, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 79}], skip=False)



    # Layer 80 => 82

    yolo_82 = _conv_block(x, [{'filter': 1024, 'kernel': 3, 'stride': 1, 'bnorm': True,  'leaky': True,  'layer_idx': 80},

                              {'filter':  255, 'kernel': 1, 'stride': 1, 'bnorm': False, 'leaky': False, 'layer_idx': 81}], skip=False)



    # Layer 83 => 86

    x = _conv_block(x, [{'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 84}], skip=False)

    x = UpSampling2D(2)(x)

    x = concatenate([x, skip_61])



    # Layer 87 => 91

    x = _conv_block(x, [{'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 87},

                        {'filter': 512, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 88},

                        {'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 89},

                        {'filter': 512, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 90},

                        {'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 91}], skip=False)



    # Layer 92 => 94

    yolo_94 = _conv_block(x, [{'filter': 512, 'kernel': 3, 'stride': 1, 'bnorm': True,  'leaky': True,  'layer_idx': 92},

                              {'filter': 255, 'kernel': 1, 'stride': 1, 'bnorm': False, 'leaky': False, 'layer_idx': 93}], skip=False)



    # Layer 95 => 98

    x = _conv_block(x, [{'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True,   'layer_idx': 96}], skip=False)

    x = UpSampling2D(2)(x)

    x = concatenate([x, skip_36])



    # Layer 99 => 106

    yolo_106 = _conv_block(x, [{'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True,  'leaky': True,  'layer_idx': 99},

                               {'filter': 256, 'kernel': 3, 'stride': 1, 'bnorm': True,  'leaky': True,  'layer_idx': 100},

                               {'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True,  'leaky': True,  'layer_idx': 101},

                               {'filter': 256, 'kernel': 3, 'stride': 1, 'bnorm': True,  'leaky': True,  'layer_idx': 102},

                               {'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True,  'leaky': True,  'layer_idx': 103},

                               {'filter': 256, 'kernel': 3, 'stride': 1, 'bnorm': True,  'leaky': True,  'layer_idx': 104},

                               {'filter': 255, 'kernel': 1, 'stride': 1, 'bnorm': False, 'leaky': False, 'layer_idx': 105}], skip=False)



    model = Model(input_image, [yolo_82, yolo_94, yolo_106])  #앵커 사이즈 3개 이므로 3개의 출력

    return model

 

YOLOv3는 106개의 층을 사용하기 때문에 for문으로 이전에 만들었던 Conv_block 함수를 호출하여 모델을 생성한다.

출력은 3개로 되있고 각각 13 , 26 , 52의  층을 가진다

 

net_h, net_w = 416, 416 # 입력할 이미지 크기

# obj_trresh : 객체와 객체가 아닌 객체를 구분하는 임계값

obj_thresh =  0.5 

# nms_thresh : 두 개의 객체가 얼마나 겹치는지 확인하는 임계값

nms_thresh  = 0.45 # 

anchors = [[116,90,  156,198,  373,326],  [30,61, 62,45,  59,119], [10,13,  16,30,  33,23]] #앵커박스 설정

#80개의 라벨데이터

labels = ["person", "bicycle", "car", "motorbike", "aeroplane", "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", "sofa", "pottedplant", "bed", "diningtable", "toilet", "tvmonitor", "laptop", "mouse", \

              "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", \

              "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"]

 

이번에는 학습에 필요한 변수들, 라벨, 앵커박스를 선언합니다.

YOLO v3 넘오면서 이미지 크기가 바뀌게 됬는데  YOLO v1에서는 이미지의 크기가 448 x 448 였고 이미지를 나눌때 중간 셀이 4개가 나오게 되고 중간 셀이 많게 되면 인식률이 떨어진다.  이를 해결하기 위해 이미지 크기를 416 x 416으로 수정 하여 중간셀이 1개만 나오게 했다.

이미지 크기를 설정하는 변수 밑에는 각각 객체를 판별할 수 있는 임계값,  두 객체간의 얼마나 겹쳐는지 확인하는 임계값, 앵커박스를 설정했다.

라벨이라는 클래스(classes)는 80개 정도로 설정 되었는데 이미지넷의 라벨은 1000개 정도이지만 ms coco는 80개다.

그래서 yolo는 80개의 라벨 값을 가지고 있다.

 

# 코코 데이터의 80개의 클래스 예측

yolov3 = make_yolov3_model()



# 코코모델로 학습된 가중치(weight) 불러오기

weight_reader = WeightReader('yolov3.weights')

weight_reader.load_weights(yolov3)

 

이제는 선언된 함수를 이용하여 모델을 만들고 가중치를 부른다.

그러면 아까 다운받았던 파일에 가중치를 불러오게 된다.

 

from numpy import expand_dims

def load_image_pixels(filename, shape):

    # 이미지 불러오기 및 이미지 사이즈 받기

    image = load_img(PATH+filename)

    width, height = image.size

    # 이미지를 416 x 416 크기로 다시 불러오기

    image = load_img(PATH+filename, target_size=shape)

    # 이미지를 numpy로 변경

    image = img_to_array(image)

    # 이미지를 0 ~ 1 사이의 값으로 변경

    image = image.astype('float32')

    image /= 255.0

    # 이미지에 차원을 추가합니다. 

    image = expand_dims(image, 0)

    return image, width, height #리사이즈 이미지, 원래 크기를 리턴

 

이미지를 불러오는 함수다.

함수를 호출을 하게되면 이미지를 2번 불러오는데 

처음에는 원본을 가져오고 크기를 기억을 하고  416x416 크기로 다시 이미지를 불러온다

416 x 416로 불러온 이미지와 원래  이미지의 크기를 반환한다

 

class BoundBox:

    def __init__(self, xmin, ymin, xmax, ymax, objness = None, classes = None):

        self.xmin = xmin

        self.ymin = ymin

        self.xmax = xmax

        self.ymax = ymax

        

        self.objness = objness

        self.classes = classes



        self.label = -1

        self.score = -1



    def get_label(self):

        if self.label == -1:

            self.label = np.argmax(self.classes)

        

        return self.label

    

    def get_score(self):

        if self.score == -1:

            self.score = self.classes[self.get_label()]

            

        return self.score

 

바운딩 박스를 클래스로 지정했다

박스의 크기를 저장하고 라벨과 정확도를 저장하는 클래스다.

 

#박스간의 교집합 계산

def _interval_overlap(interval_a, interval_b):

    x1, x2 = interval_a

    x3, x4 = interval_b



    if x3 < x1:

        if x4 < x1:

            return 0

        else:

            return min(x2,x4) - x1

    else:

        if x2 < x3:

             return 0

        else:

            return min(x2,x4) - x3 

 

박스와 객체와의 교집합 계산하는 함수다

IoU에서 계산에 사용되는 함수며 설명은 IoU할 때 하겠습니다.

 

#2개의 객체를 IoU를 통해서 비교 함수

def bbox_iou(box1, box2):

    intersect_w = _interval_overlap([box1.xmin, box1.xmax], [box2.xmin, box2.xmax])

    intersect_h = _interval_overlap([box1.ymin, box1.ymax], [box2.ymin, box2.ymax])

    

    intersect = intersect_w * intersect_h



    w1, h1 = box1.xmax-box1.xmin, box1.ymax-box1.ymin

    w2, h2 = box2.xmax-box2.xmin, box2.ymax-box2.ymin

    

    union = w1*h1 + w2*h2 - intersect

    

    return float(intersect) / union

 

오브젝트 디텍션의 평가요소인 IoU(Intersection Over Union)입니다. 

IoU는 교집합 영역 넓이 / 합집합 영역 넓이를 구하는 평가 방법입니다.

해당 작업을 하는 이유는 하나의 객체에서 하나의 박스만 나오는 것이 아닌 많은 양의 박스들이 나오게 됩니다.

그래서 박스와 객체간의 겹침이 많은 것을 찾기 위해서 사용하는 것입니다.

 

def do_nms(boxes, nms_thresh):

    if len(boxes) > 0: 

        nb_class = len(boxes[0].classes)  #80개 라벨(클래스)

    else: 

        return 



    #for문으로 80개의 라벨(클래스) 파악

    for c in range(nb_class):

        # 80개의 라벨(클래스)를 순위를 매긴다.

        sorted_indices = np.argsort([-box.classes[c] for box in boxes])



        # 박스가 생성된 객체 수 --> len(sorted_indices) 

        for i in range(len(sorted_indices)):

            index_i = sorted_indices[i]



            # box에 데이터가 없다면 continue

            if boxes[index_i].classes[c] == 0: continue



            for j in range(i+1, len(sorted_indices)):

                index_j = sorted_indices[j]        

                # 순위를 매긴 데이터이므로 뒤에 있는 값들은 IoU를 통해 크게 일치하는 데이터들만 0으로 처리한다.

                if bbox_iou(boxes[index_i], boxes[index_j]) >= nms_thresh:

                    boxes[index_j].classes[c] = 0

 

객채들을 NMS(Non-Maximum Suppression)를 처리를 해야합니다.

객체의 많은 박스와 여러 라벨들이 측정이 되는데 그 중에서 가장 높은걸 제외하고 전부 0으로 처리합니다.  

 

#바운딩 박스 후보와 라벨(클래스)를 예측합니다.

def decode_netout(netout, anchors, obj_thresh,  net_h, net_w):

    grid_h, grid_w = netout.shape[:2] # shape (13, 13)

    nb_box = 3 

    netout = netout.reshape((grid_h, grid_w, nb_box, -1)) # shape (13, 13, 3, 85)

    nb_class = netout.shape[-1] - 5 # size 80



    boxes = []



    netout[..., :2]  = _sigmoid(netout[..., :2])

    netout[..., 4:]  = _sigmoid(netout[..., 4:])

    netout[..., 5:]  = netout[..., 4][..., np.newaxis] * netout[..., 5:]

    netout[..., 5:] *= netout[..., 5:] > obj_thresh



    for i in range(grid_h*grid_w):

        row = i / grid_w

        col = i % grid_w

        

        for b in range(nb_box):

            # 객체를 확인하는 점수

            objectness = netout[int(row)][int(col)][b][4]

            

            # 객체일 경우가 obj_thresh 보다 낮으면 컨티뉴

            if(objectness.all() <= obj_thresh): continue

            

            # x, y, w, h의 크기를 구합니다.

            x, y, w, h = netout[int(row)][int(col)][b][:4]



            x = (col + x) / grid_w # 중앙에 위치하고, 이미지의 넓이

            y = (row + y) / grid_h # 중앙에 위치하고, 이미지의 높이

            w = anchors[2 * b + 0] * np.exp(w) / net_w # 이미지의 넓이

            h = anchors[2 * b + 1] * np.exp(h) / net_h # 이미지의 높이  

            

            # 클래스(라벨)의 확률을 저장한다

            classes = netout[int(row)][col][b][5:]

            

            box = BoundBox(x-w/2, y-h/2, x+w/2, y+h/2, objectness, classes)



            boxes.append(box)



    return boxes

 

박스와 라벨을 예측하는 함수 입니다. 

이 함수를 통하여 객체에 대해 박스를 그리고, 라벨을 씌웁니다. 

하나의 객체에 대하여 여러 박스와 라벨을 만드는 작업입니다. 

 

def correct_yolo_boxes(boxes, image_h, image_w, net_h, net_w):

    # 가로 세로 크기 중 큰쪽을 자릅니다.

    if (float(net_w)/image_w) < (float(net_h)/image_h):

        new_w = net_w

        new_h = (image_h*net_w)/image_w



    else:

        new_h = net_w

        new_w = (image_w*net_h)/image_h

    #바운딩 박스의 크기를 조절

    for i in range(len(boxes)):

        x_offset, x_scale = (net_w - new_w)/2./net_w, float(new_w)/net_w

        y_offset, y_scale = (net_h - new_h)/2./net_h, float(new_h)/net_h

        

        boxes[i].xmin = int((boxes[i].xmin - x_offset) / x_scale * image_w)

        boxes[i].xmax = int((boxes[i].xmax - x_offset) / x_scale * image_w)

        boxes[i].ymin = int((boxes[i].ymin - y_offset) / y_scale * image_h)

        boxes[i].ymax = int((boxes[i].ymax - y_offset) / y_scale * image_h)

 

decode_netout 함수에서 그렸던 박스들을 이미지의 원래 크기에 맞혀 바꿔주는 함수입니다.

코드에서 예측에 사용했던 크기는 416x416이지만 실제 이미지의 크기는 해당 이미지 보다 크거나 작기 때문에 크기를 맞춰주는 함수입니다.

 

from matplotlib.patches import Rectangle

def draw_boxes(filename, v_boxes, v_labels, v_scores):

    # 이미지를 불러오기 

    data = plt.imread(PATH+filename)



    # 이미지 출력

    plt.imshow(data)



    ax = plt.gca()

    # 박스 그리기

    for i in range(len(v_boxes)):

        box = v_boxes[i]

        # 박스의 좌표를 얻기

        y1, x1, y2, x2 = box.ymin, box.xmin, box.ymax, box.xmax

        # 상자의 높이와 넓이 구하기

        width, height = x2 - x1, y2 - y1

        # 바운딩박스 모양만들기

        rect = Rectangle((x1, y1), width, height, fill=False, color='red')

        # 바운딩박스 그리기

        ax.add_patch(rect)

        # 라벨과 정확도를 이미지에 표시

        label = "%s (%.3f)" % (v_labels[i], v_scores[i])

        plt.text(x1, y1, label, color='red')

        plt.axis('off')

    # plot 표현

    plt.show()
    

 

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

이전에 예측해서 저장했던 박스의 좌표를 이미지에 그립니다.

 

def get_boxes(boxes, labels, thresh):

    v_boxes, v_labels, v_scores = list(), list(), list()

    # 모든 box데이터 검수

    for box in boxes:

        # 모든 라벨 값 검수

        for i in range(len(labels)):

            # box의 라벨 값이 임계값(0.6) 이상인 데이터만 사용

            if box.classes[i] > thresh:

                # 리턴할 리스트에 데이터 추가하기 

                v_boxes.append(box)

                v_labels.append(labels[i])

                v_scores.append(box.classes[i]*100)

                # 하나의 이미지에 여러개의 박스가 있기 때문에 break는 하지 않습니다. 

    return v_boxes, v_labels, v_scores

 

설정된 박스의 임계값이 0.6이상인 값들을 추출하는 함수 입니다.

박스정보, 라벨, 점수를 반환을 하는 것입니다.

 

for j in os.listdir(PATH) : 

  if 'jpg' in j : 

    # 디텍션에 사용할 이미지 크기 정하기

    input_w, input_h = 416, 416

    # 폴더 안에 이미지 파일 이름 지정

    photo_filename = j

    # 이미지 프리프로세싱 하기

    image, image_w, image_h = load_image_pixels(photo_filename, (net_w, net_w))



    # 예측값 만들기

    yolos = yolov3.predict(image)

    # 리스트로된 배열을 요약

    print([a.shape for a in yolos])



    # 앵커박스 정의

    anchors = [[116,90, 156,198, 373,326], [30,61, 62,45, 59,119], [10,13, 16,30, 33,23]]



    # 오브젝트 디텍션의 임계값

    class_threshold = 0.6 

    boxes = list()



    for i in range(len(yolos)):

        # decode_netout으로 예측한 이미지, 앵커박스, 객체구별임계값, 이미지 크기를 보낸다

        boxes += decode_netout(yolos[i][0], anchors[i], obj_thresh,  net_h, net_w)



    # 바운딩 박스 크기 수정

    correct_yolo_boxes(boxes, image_h, image_w, net_h, net_w)



    # 최대값이 아닌 상자들을 0으로 초기화

    do_nms(boxes, nms_thresh)



    # 오브젝트 디텍션 데이터 얻기

    v_boxes, v_labels, v_scores = get_boxes(boxes, labels, class_threshold)

    # 찾은 객체 라벨과 스코어 출력

    for i in range(len(v_boxes)):

        print(v_labels[i], v_scores[i])

    # 이미지의 박스를 같이 출력

    draw_boxes(photo_filename, v_boxes, v_labels, v_scores)

 

for문을 이용하여 이미지를 입력받아 위에서 선언한 함수들을 호출하여 이미지에 박스와 라벨을 붙여 출력을 하게 됩니다.

 

실제로 코드를 실행하여 결과를 확인해보세요

 

 


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