본문 바로가기

(인프런)딥러닝 컴퓨터비전 완벽 가이드_권철민

[인프런] 3. Object Detection과 Segmentation을 위한 주요 데이터 세트 및 구현 패키지 소개 그리고 OpenCV

728x90

1. Object Detection 주요 데이터 세트 소개 및 Pascal VOC 데이터 세트의 이해

  • Object Detection 과 segmentation 딥러닝 패키지는 주로 아래 3가지 데이터 세트 기반.
    • Pascal VOC
    • MS COCO
    • Google Open Images
  • 아래 갈 수록 더 세분화되어 많은 카테고리를 갖고 있다.
  • Annotation?
    • 바운딩박스의 위치, 이름, 이미지 사이즈 등 이미지에 대한 정보를 가지는 주석
  • Pascal VOC의 경우 Annotation의 정보를 담은 xml 파일과 원본이미지 파일인 jpg 파일이 같은 이름을 갖는다.

2. MS COCO

  • MS COCO는 Object detection에서 일반적으로 사용되는 데이터 셋
    • train, val, test 셋을 따로 다운받을 수 있으며, 그에 대한 images 파일(파일명, 이미지크기 등), Annotation(bbox, category 등)은 모두 통합된 형태의 파일로서 존재한다.
    • COCO는 이미지 한개에 여러 object 을 가지고 있으며 타 데이터셋에 비해 난이도가 높다.

3. OpenCV 개요

  • 방대한 라이브러리 보유
  • 주의점
    • OpenCV는 OS 의 window frame 과 바로 인터페이스하여 사용할 수 있는 여러 기능 제공. 하지만 이들 기능을 사용하려면 window frame 생성이 가능한 GUI 개발 환경에서만 가능(window GUI, Linux X-windows 등). 따라서 주피터 노트북 기반에서는 사용시 오류 발생
    • 즉, 아래와 같은 코드들은 주피터 노트북에서 실행 시 오류가 남. window 창을 생성할 환경이 안됨.
      • ex) cv2.imshow, cv2.waitkey(), cv2.destroyAllWindows()
      • 주피터 노트북은 이미지 배열(array) 시각화에 matplotlib 을 사용.

4. OpenCV 를 활용한 이미지 처리 실습

  • skimage, PIL, opencv 등 여러가지 라이브러리로 이미지를 시각화할 수 있다.
  • 그 중에서도 opencv는 여러 api를 제공하여 이미지, 영상처리에 특화됨.
  • 자세한 건 아래 참조

5. OpenCV 를 활용한 영상 처리 실습

  • 개요
    • 기본적으로 VideoCapture(video파일위치) 클래스와 VideoWriter(write할 위치, 인코딩codec유형, fps, frame크기) 클래스가 핵심
    • 전자로 input 하여 읽고, 후자로 output 한다고 생각하면 됨.
    • fps 가 작으면 초당프레임이 작아지므로 딱딱 끊겨서 표현됨.
    • 그리고 VideoWriter()을 이용해 지정하는 codec(DIVX, XVID, MJPG, X264, WMV1, WMV2) 로 영상을 인코딩할 수 있음.
    • 참고로 리눅스 서버에서는 반드시 XVID 형태로만 인코딩 적용하고 write 시 동영상 명의 확장자는 반드시 .avi로만 해야함.

OpenCV의 이미지와 영상 처리

  • Python에서 사용되는 여러 image라이브러리를 간단히 살펴보고 OpenCV와의 차이 이해
  • OpenCV의 단일 이미지 처리 방식 이해
  • OpenCV의 비디오 영상 처리 방식 이해

아래 예제는 GPU가 필요하지 않으며 코랩 커널을 GPU로 바꿀 필요가 없습니다.

  • tensorflow와 keras 설치는 필요하지 않습니다.
  • OpenCV와 기타 필요한 패키지는 이미 코랩에 설치 되어 있습니다.

실습 코드를 github에서 다운로드

# 현재 디렉토리는 /content이며 이 디렉토리를 기준으로 실습코드와 데이터를 다운로드 합니다. 
!pwd

# 실습코드를 github에서 다운로드
!git clone https://github.com/chulminkw/DLCV.git

# DLCV 디렉토리가 Download되고 DLCV 밑에 Detection과 Segmentation 디렉토리가 있는 것을 확인
!ls -lia 
!ls -lia DLCV
/content
Cloning into 'DLCV'...
remote: Enumerating objects: 75, done.
remote: Counting objects: 100% (75/75), done.
remote: Compressing objects: 100% (71/71), done.
remote: Total 234 (delta 32), reused 0 (delta 0), pack-reused 159
Receiving objects: 100% (234/234), 142.72 MiB | 22.96 MiB/s, done.
Resolving deltas: 100% (89/89), done.
total 20
4456463 drwxr-xr-x 1 root root 4096 Mar  7 16:14 .
4197035 drwxr-xr-x 1 root root 4096 Mar  7 16:13 ..
3670031 drwxr-xr-x 4 root root 4096 Mar  1 14:35 .config
4196981 drwxr-xr-x 7 root root 4096 Mar  7 16:14 DLCV
4456464 drwxr-xr-x 1 root root 4096 Mar  1 14:35 sample_data
total 11016
4196981 drwxr-xr-x 7 root root    4096 Mar  7 16:14  .
4456463 drwxr-xr-x 1 root root    4096 Mar  7 16:14  ..
4197178 drwxr-xr-x 2 root root    4096 Mar  7 16:14  colab_tf115_modify_files
4197181 drwxr-xr-x 6 root root    4096 Mar  7 16:14  data
4197064 drwxr-xr-x 8 root root    4096 Mar  7 16:14  Detection
4197049 -rw-r--r-- 1 root root 6567662 Mar  7 16:14  DLCV_Colab_SrcCode_20200905.zip
4196982 drwxr-xr-x 8 root root    4096 Mar  7 16:14  .git
4197215 -rw-r--r-- 1 root root 2063693 Mar  7 16:14  labelimg.pptx
4197216 -rw-r--r-- 1 root root 2612271 Mar  7 16:14 '구글클라우드 가입하기.pdf'
4197168 -rw-r--r-- 1 root root     142 Mar  7 16:14  README.md
4197169 drwxr-xr-x 3 root root    4096 Mar  7 16:14  Segmentation

OpenCV 이미지 처리 이해 및 타 패키지 비교

PIL 패키지를 이용하여 이미지 로드하기

import matplotlib.pyplot as plt
import os
%matplotlib inline

from PIL import Image

# PIL은 oepn()으로 image file을 읽어서 ImageFile객체로 생성. 
# 코랩 버전은 상대 경로를 사용하지 않습니다. /content 디렉토리를 기준으로 절대 경로를 이용합니다. 
# default_dir 은 /content/DLCV로 지정하고 os.path.join()으로 상세 파일/디렉토리를 지정합니다. 
default_dir = '/content/DLCV'
pil_image = Image.open(os.path.join(default_dir, "data/image/beatles01.jpg"))
print('image type:', type(pil_image))

plt.figure(figsize=(10, 10))
plt.imshow(pil_image)
#plt.show()
image type: <class 'PIL.JpegImagePlugin.JpegImageFile'>





<matplotlib.image.AxesImage at 0x7f28f5fba3d0>

skimage(사이킷이미지)로 이미지 로드 하기

  • skimage는 imread()를 이용하여 RGB 원본 이미지를 RGB 형태의 넘파이 배열로 반환함.
from skimage import io

#skimage는 imread()를 이용하여 image를 numpy 배열로 반환함. 
sk_image = io.imread(os.path.join(default_dir, "data/image/beatles01.jpg"))
print('sk_image type:', type(sk_image), ' sk_image shape:', sk_image.shape)
# shape은 (행, 열, 채널) 이므로 행 = 세로, 열 = 가로길이가 된다.
plt.figure(figsize=(10, 10))
plt.imshow(sk_image)
#plt.show()
sk_image type: <class 'numpy.ndarray'>  sk_image shape: (633, 806, 3)





<matplotlib.image.AxesImage at 0x7f28e9ef2b90>

OpenCV로 이미지 로드하기

  • OpenCV는 imread()를 이용하여 원본 RGB 이미지를 BGR 형태의 넘파이 배열로 반환함.
  • OpenCV의 imwrite()를 이용한다면 BGR 형태의 이미지 배열을 파일에 기록할 때 다시 RGB형태로 변환하므로 사용자는 RGB->BGR->RGB 변환에 신경쓰지 않아도 됨.
import cv2

cv2_image = cv2.imread(os.path.join(default_dir, "data/image/beatles01.jpg"))
# data/output 위치에 beatles02_cv.jpg 라는 이름으로 cv2_image 가 저장됨. 
cv2.imwrite(os.path.join(default_dir, "data/output/beatles02_cv.jpg"), cv2_image)
print('cv_image type:', type(cv2_image), ' cv_image shape:', cv2_image.shape)

plt.figure(figsize=(10, 10))
# plt.imread 를 통해 array 뿐만아니라 이미지 파일도 읽을 수 있음. 
# 즉, plt.imread 를 통해 이미지 파일을 저장하고 imshow 를 통해 시각화
# plt.imread 는 cv2.imread 와 다르게 RGB로 저장하므로 원본 그대로 표현 가능.
img = plt.imread(os.path.join(default_dir, "data/output/beatles02_cv.jpg"))
plt.imshow(img)
#plt.show()
cv_image type: <class 'numpy.ndarray'>  cv_image shape: (633, 806, 3)





<matplotlib.image.AxesImage at 0x7f28e0c68810>

 

OpenCV의 imread()로 반환된 BGR 이미지 넘파이 배열을 그대로 시각화 하기

  • OpenCV의 imread()는 RGB를 BGR로 변환하므로 원하지 않는 이미지가 출력됨
cv2_image = cv2.imread(os.path.join(default_dir, "data/image/beatles01.jpg"))

plt.figure(figsize=(10, 10))
plt.imshow(cv2_image)
plt.show()

png

)

sk_image = io.imread(os.path.join(default_dir, "data/image/beatles01.jpg"))
print(sk_image.shape)
# 만약 plt.imshow(sk_image[:,:,0]) 을 하면, 2차원 형태로 나오므로 흑백으로 나옴.
sk_image[:, :, 0]
(633, 806, 3)





array([[ 18,  17,  18, ...,  46,  38,  63],
       [ 18,  18,  18, ...,  72,  41,  37],
       [ 18,  18,  18, ...,  84,  56,  42],
       ...,
       [225, 226, 228, ..., 231, 228, 229],
       [225, 225, 226, ..., 229, 229, 227],
       [225, 225, 224, ..., 227, 227, 227]], dtype=uint8)
cv2_image = cv2.imread(os.path.join(default_dir, "data/image/beatles01.jpg"))
print(type(cv2_image))
print(cv2_image.shape)
cv2_image[:, :, 0]
<class 'numpy.ndarray'>
(633, 806, 3)





array([[ 19,  19,  20, ...,  47,  39,  64],
       [ 20,  20,  20, ...,  71,  40,  36],
       [ 20,  20,  20, ...,  82,  54,  40],
       ...,
       [198, 199, 201, ..., 190, 189, 188],
       [198, 198, 199, ..., 188, 188, 186],
       [199, 199, 198, ..., 186, 186, 186]], dtype=uint8)
cv2_image[:, :, 2]
array([[ 18,  18,  18, ...,  47,  39,  64],
       [ 19,  19,  18, ...,  72,  41,  37],
       [ 18,  18,  18, ...,  84,  56,  41],
       ...,
       [225, 226, 228, ..., 231, 230, 229],
       [225, 225, 226, ..., 229, 229, 227],
       [225, 225, 224, ..., 227, 227, 227]], dtype=uint8)
cv2_image = cv2.imread(os.path.join(default_dir, "data/image/beatles01.jpg"))
draw_image = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2RGB)

plt.figure(figsize=(10, 10))
plt.imshow(draw_image)
plt.show()

png

BGR 을 RGB 로 바꾸어야 하는 수고가 있음에도 불구하고 opencv를 사용하는 이유는 opencv가 여러가지 API 를 제공하며 여러 가속화 기능을 탑재하고 있어서 이미지, 영상 처리에 특화되어 있기 때문.

OpenCV 영상처리

  • OpenCV는 간편하게 비디오 영상처리를 할 수 있는 API를 제공
  • VideoCapture 객체는 Video Streaming을 Frame 별로 Capture하여 처리할 수 있는 기능 제공
  • VideoWriter 객체는 VideoCapture로 읽어들인 Frame을 동영상으로 Write하는 기능 제공
# 코랩 버전은 아래 코드를 이용합니다.
# 코랩 버전은 용량 문제로 인해 Night and Day를 browser로 보여줄때 커널이 종료됩니다. 보다 작은 크기인 John Wick으로 대체
from IPython.display import HTML
from base64 import b64encode
mp4 = open('/content/DLCV/data/video/John_Wick_small.mp4','rb').read()
data_url = "data:video/mp4;base64," + b64encode(mp4).decode()
HTML("""
<video width=800 controls>
      <source src="%s" type="video/mp4">
</video>
""" % data_url)
# 코랩 버전에서 위의 sample은 John wick이지만 실제 Video 처리는 강의와 동일하게 Night and Day로 수행. 

import cv2

default_dir = '/content/DLCV'
video_input_path = os.path.join(default_dir, 'data/video/Night_Day_Chase.mp4')
# linux에서 video output의 확장자는 반드시 avi 로 설정 필요. 
video_output_path = os.path.join(default_dir, 'data/output/Night_Day_Chase_output.avi')

cap = cv2.VideoCapture(video_input_path)
# Codec은 *'XVID'로 설정. 
codec = cv2.VideoWriter_fourcc(*'XVID')

# VideoWriter 클래스의 vid_size 인자에는 int 자료형이 입력되어야 함. 
# round 를 해주면 int 자료형을 반환함. 
vid_size = (round(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))) #(200, 400)
vid_fps = cap.get(cv2.CAP_PROP_FPS )

vid_writer = cv2.VideoWriter(video_output_path, codec, vid_fps, vid_size) 

frame_cnt = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print('총 Frame 갯수:', frame_cnt, 'FPS:', round(vid_fps), 'Frame 크기:', vid_size)
총 Frame 갯수: 1383 FPS: 28 Frame 크기: (1216, 516)

아래 과정은 VideoCapture 를 통해 영상을 읽고 frame 마다 일정한 사각형을 그려주고 마지막에 VideoWriter 를 통해 output 하는 작업이다.

따라서 영상처리의 매커니즘은

  • 영상을 읽고(VIdeoCapture)
  • 처리하고(While True~)
  • output하는 것이다(VideoWriter)
import time

green_color=(0, 255, 0)
red_color=(0, 0, 255)

start = time.time()
index=0
while True:
  # cap.read() 는 처리할 프레임의 존재유무, 프레임 이미지 두가지를 반환
    hasFrame, img_frame = cap.read()
    if not hasFrame:
        print('더 이상 처리할 frame이 없습니다.')
        break
    index += 1
    print('frame :', index, '처리 완료')
    cv2.rectangle(img_frame, (300, 100, 800, 400), color=green_color, thickness=2)
    caption = "frame:{}".format(index)
    cv2.putText(img_frame, caption, (300, 95), cv2.FONT_HERSHEY_SIMPLEX, 0.7, red_color, 1)
    # write 을 하면서 RGB 로 되기 때문에 변환과정이 필요없음. 
    vid_writer.write(img_frame)

print('write 완료 시간:', round(time.time()-start,4))

# release 를 통해 open 된 비디오파일이나 영상장치를 닫는다. 
# read 로 open 했으니 닫아주는 듯. 
# 마지막에 그냥 해주는게 좋은 듯. 
vid_writer.release()
cap.release()   
frame : 1 처리 완료
frame : 2 처리 완료
frame : 3 처리 완료
frame : 4 처리 완료
....
frame : 1378 처리 완료
frame : 1379 처리 완료
frame : 1380 처리 완료
frame : 1381 처리 완료
frame : 1382 처리 완료
frame : 1383 처리 완료
더 이상 처리할 frame이 없습니다.
write 완료 시간: 15.0719
## colab 버전은 영상 파일을 google drive에서 download 해야 합니다. 이를 위해 google drive를 colab에 mount 수행. 
import os, sys 
from google.colab import drive 

drive.mount('/content/gdrive')

코랩에서 등장한(?) 데이터는 google drive 로 옮겨야 실체화(?)된다. 즉, 나중에 download 할 수 있다.
이를 위해서는 mount를 하고 !cp(copy) [옮길파일] [My Drive/파일명] 을 실행한다.

## colab 버전은 Object Detection 적용된 영상 파일을 google drive에서 download 해야 합니다. 
## My Drive 디렉토리 이름에 공란이 있으므로 ' '로 묶습니다. 
# 이를 통해 이제 Night_Day 영상이 내 구글 드라이브에 저장됨. 
!cp /content/DLCV/data/output/Night_Day_Chase_output.avi '/content/gdrive/My Drive/Night_Day_Chase_output.avi'

6.  Object Detection과 Segmentation을 구현한 오픈소스, OpenCV, Tensorflow 기반의 다양한 패키지 소개

  • 종류
    • Keras, tensorflow 기반 패키지 : 쉽다. 
      • ex) keras-yolo3, keras-retina, mask r-cnn : 회사들에서 open 소스로 제공.
    • opencv의 dnn 모듈 : 간편하게 object detection inference 가능. 학습 x, GPU 활용 어려움. 미리 학습된 모델 기반. 
    • tensorflow object detection api : 가장 많은 알고리즘 적용 가능. 어렵다.
      • mobile 환경(연산능력이 약한 환경에서)에서의 object detection에 집중.
      • 학습과정이 디게 복잡하고 version2와 1이 충돌. document 도 부족.

7. Deep Learning을 위한 GPU 이해 - CUDA와 cuDNN 소개 및 nvidia-smi 명령어 활용법

 

  • CUDA란
    • GPU 를 C 혹은 Python 과 같은 언어로 제어할 수 있게끔하는 기술.
  • cuDNN 은 CUDA 의 딥러닝 라이브러리이다. 본래 GPU 는 그래픽카드의 용도였으므로 딥러닝 네트워크를 구성하고 학습시키기 위한 라이브러리를 출시한 것.

  • 즉, tensorflow -> cuDNN -> CUDA 순으로 접근하고 종국적으로 GPU 를 제어한다.
  • 참고로 리눅스에는 이런 것들이 다 안깔려있지만, 구글클라우드플랫폼에서 'Deeplearning on Linux' 로 가상환경을 구성했다면 다 깔려있음. 
  • nvidia-smi 를 통해 GPU 구동현황을 알 수 있으며, 이 명령어가 안된다면 CUDA 설치에 오류가 있는 것.

8. Object Detection 네트웍 개요 및 FPS/Resolution/성능 상관관계

  • object detection 네트워크는 feature extractor, object detection network, region proposal 세가지로 구성된다.
    • 첫번째는 우리가 아는 cnn을 통한 feature map을 만드는 것.
    • 두번째는 feature map 을 기반으로 object detect 하는 것.
    • 세번째는 selective search 와 같은 region proposal 을 활용하는 것.
  •  Image resolution(이미지의 크기)가 커지면 frame 의 선명도가 높아지므로 detection 성능은 좋아진다. 반면 배열의 크기가 커지므로 fps(초당 프레임처리 수)는 낮아짐. 
  • 두마리의 토끼를 잡을 수는 없으므로 상황에 맞는 모델을 선택해야 함. 

질문과 비판은 언제나 환영입니다. 많이 꾸짖어주세요.

 

728x90