본문 바로가기
🖊️Data Analysis/📌 DL)영상 분석

[OpenCV & Yolo] 이미지에 있는 객체 인식

by 빛나고요 2021. 7. 23.
BIG

Study. OpenCV

  • Yolo를 사용하여 영상 내에서 사물 인식하도록 하는 공부 전에 이미지 내에 있는 객체를 인식하는 먼저 공부해보았다. 
    • Python
    • OpenCV
    • Yolo
1. 필요한 라이브러리 import
import cv2
import numpy as np
  • OpenCV와 numpy를 불러온다. 
  • OpenCV
    • 실시간 이미지 프로세싱에 중점을 둔 프로그래밍 라이브러리(Tensorflow, Torch/PyTorch, Caffe 등의 딥러닝 프레임워크 지원)
2. Yolo 불러오기
# Yolo 로드
net = cv2.dnn.readNet("yolov3.weights", "yolov3.cfg") # weights, cfg파일을 불러와서 yolo의 네트워크와 연결한다. 
classes = [] #class 배열 만들기
with open("coco.names", "r") as f: # coco 파일을 읽어온다. 
    classes = [line.strip() for line in f.readlines()]  # 읽어온 coco 파일을 whitespace(공백라인)를 제거하여 classes 배열 안에 넣는다.
layer_names = net.getLayerNames() # 네트워크의 모든 레이어 이름을 가져와서 layer_names에 넣는다.
output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()] # 레이어 중 출력 레이어의 인덱스를 가져와서 output_layers에 넣는다.

colors = np.random.uniform(0, 255, size=(len(classes), 3)) # 클래스의 갯수만큼 랜덤으로 BRG 배열을 생성한다. 한 사물 당 하나의 color만 사용할 수 있도록 해서 구분해야한다.
  • yolov3.weights : yolov3의 훈련된 가중치를 저장하고 있는 이진 파일
  • yolov3.cfg :  yolov3의 네트워크 구성을 저장하고 있는 텍스트 파일

 

  • strip() : whitespace(띄어쓰기, 탭, 엔터)를 없애는 것, 중간에 끼어있는 것은 없어지지 않는다.
  • getLayerNames() : 네트워크의 모든 레이어 이름을 가져온다. 
    • YOLOv3에는 3개의 출력 레이어(82, 94, 106)가 있다.
  • getUnconnectedOutLayers() : 출력 레이어를 가져온다.
  • np.random.uniform(a, b) : random 모듈 안에 정의되어 있는 두 수 사이의 랜덤 한 소수를 리턴 시켜주는 함수이다.

 

3. 이미지 불러오기
# 이미지 가져오기
img = cv2.imread("sample2.jpg") # opencv를 통해 이미지를 가져온다.
img = cv2.resize(img, None, fx=0.4, fy=0.4) # 이미지 크기를 재설정한다. 
height, width, channels = img.shape # 이미지의 속성들을 넣는다.
  • imread('ooo.jpg', cv2.IMREAD_ooo)
    • IMREAD_COLOR : BRG로 읽어온다. (OpenCV는 RGB 3개의 채널을 BRG 순서로 읽어온다.)
    • IMREAD_GRAYSCALE :  흑백으로 읽어온다. 
    • IMREAD_UNCHANGED :  파일 속성 그대로 읽어온다. 

 

4. 물체 감지
# 물체 감지
blob = cv2.dnn.blobFromImage(img, 0.00392, (416, 416), (0, 0, 0), True, crop=False) # 이미지를 blob 객체로 처리한다.
net.setInput(blob) # blob 객체에 setInput 함수를 적용한다. 
outs = net.forward(output_layers) #output_layers를 네트워크 순방향으로 실행(추론)한다.
  • cv2.dnn.blobFromImage(img, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
    • => cv2.dnn.blobFromImage(image, scalefactor=None, size=None, mean=None, swapRB=None, crop=None, ddepth=None)
      • image : 입력 영상  [img]
      • scalefactor : 입력 영상 픽셀 값에 곱할 값. 기본값은 1   [0.00392]
        • 0~1 사이로 정규화를 해서 지정해주어야 한다. 
      • size : 출력 영상의 크기. 기본값은 (0, 0)   [416, 416]
      • mean : 입력 영상 각 채널에서 뺄 평균값. 기본값은 (0, 0, 0, 0)   [0, 0, 0]
      • swapRB : R과 B채널을 서로 바꿀 것인지 결정하는 플래그. 기본값은 False  [True]
      • crop : 크롭(crop) 수행 여부. 기본값은 False   [False]

 

5. 물체 인식 정보를 화면에 표시
  • 물체의 범위를 박스 형태로 정확하게 잡아주고 그 물체가 무엇인지 labeling 해주어야 한다. 
  • labeling하는 기준은 신뢰도(confidence)가 0.5를 넘으면 물체라고 인식을 한다. 
#정보를 화면에 표시
class_ids = []  # 인식한 사물 클래스 아이디를 넣는 배열
confidences = [] # 0에서 1까지 사물 인식에 대한 신뢰도를 넣는 배열
boxes = [] # 사물을 인식해서 그릴 상자에 대한 배열 
for out in outs:
    for detection in out:
        scores = detection[5:] #
        class_id = np.argmax(scores) # scores 중에서 최대값을 색인하여 class_id에 넣는다.
        confidence = scores[class_id] # scores 중에서 class_id에 해당하는 값을 confidence에 넣는다.
        if confidence > 0.5:  # 만약 정확도가 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)

 

 

6. 노이즈 제거
#노이즈 제거
indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
  • 같은 사물에 대해서 박스가 여러 개인 것을 제거하는 Non Maximum Suppresion 작업을 한다.

 

7. 결과 이미지 출력
font = cv2.FONT_HERSHEY_PLAIN # Font종류 중 하나인 FONT_HERSHEY_PLAIN(작은 크기 산세리프 폰트)를 적용한다.
for i in range(len(boxes)): 
    if i in indexes:
        x, y, w, h = boxes[i] 
        label = str(classes[class_ids[i]]) # 클래스 아이디 지정해둔 것을 label변수에 저장
        color = colors[i] #위에서 colors배열에 색상을 넣어둔 것을 color에 저장
        cv2.rectangle(img, (x, y), (x + w, y + h), color, 2) # 사각형을 그린다. 
        cv2.putText(img, label, (x, y + 30), font, 3, color, 3) # yolo에서 학습된 사물의 명칭을 출력한다.
cv2.imshow("Image", img) # 이미지 출력
cv2.waitKey(0) # waitKey() : 0 -> 키 입력이 있을 때까지 무한 대기한다. / ms 단위로 입력하면 그 단위에 따라 대기한다.
cv.destroyAllWindows() # 열린 모든 창을 닫는다.
  • 모든 정보를 추출해서 화면에 표시한다.
  • cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
    • [img] : 이미지 파일
    • [(x, y)] : 시작점 좌표
    • [(x+w, y+h)] :  종료점 좌표
    • [color] : 색상(BGR)
    • [2] : 선 두께 / 값을 -1로 주면 내부가 색칠된 사각형을 얻을 수 있다.
  •  cv2.putText(img, label, (x, y + 30), font, 3, color, 3)
    • [img] : 이미지 파일
    • [label] : 출력할 텍스트 => yolo에서 미리 학습된 사물의 이름을 label 변수에 넣어뒀는데 그것을 출력
    • [(x, y + 30)] : 텍스트 이미지의 좌하단 꼭짓점
    • [font] : fontFace. 폰트 스타일 지정 => 아까 font변수에 넣었던 폰트 스타일로 출력
    • [3] : fontScale. 폰트 크기를 3으로 지정
    • [color] : BGR 값으로 색상을 채움 => colors 배열에 저장했던 BGR 값을 color에 다시 저장해뒀는데 그것을 색상으로 사용
    • [3] : thickness. 폰트 굵기를 3으로 지정

 

8. 결과

  • 사람, 개, 컵 등이 인식된 것을 알 수 있다. 

댓글