【Python-Module】 비전 처리 라이브러리 활용 - OpenCV 뼈대 코드
다양한 비전 라이브러리를 잠시만 공부해보고, OpenCV를 집중적으로 공부해보자. 이미지 뿐만 아니라 동영상까지 다루는 방법에 대해서, 앞으로 계속 사용할 뼈대 코드들에 대해서 공부해본다.
DLCV/Detection/preliminary/OpenCV이미지와 영상처리 개요 파일 참조
1. Python 기반 주요 이미지 라이브러리
- PIL(Python Image Library) : 주요 이미지 처리만. 처리 성능이 상대적으로 느림. 망해가는 모듈.
- Scikit-Image : Scipy기반. Numpy기반.
- OpenCV
- 최고 인기 컴퓨터 비전 라이브러리. 컴퓨터 비전 기능 일반화에 크게 기여. 대중화.
- cv2.imread(“파일명”) -> numpy array로 변환해 저장 -> RGB형태가 아닌 BGR형태로 로딩!!
따라서 image를 바꿔줘야 한다.
import cv2 import matplotlib.pyplot as plt bgr_img_array = cv2.imread('file Name') rgb_img_array = cv2.cvtColor(bgr_img_arry, cv2.COLOR.BGR2RGB) plt.imshow(rgb_img_array)
근데 imwrite에서는 내부적으로 또 RGB로 바꾸기 때문에 주의해야한다.
bgr_img_array = cv2.imread('file Name') rgb_img_array = cv2.cvtColor(bgr_img_arry, cv2.COLOR.BGR2RGB) cv2.imwirte('저장할 파일 이름', bgr_img_array) # 따라서 bgr이미지 넣어야함
- OpenCV Windows Frame 인터페이스 : 아래와 같은 기능들은 GUI 윈도우 우분투 환경에서만 쓸 수 있기 때문에 주피터 노트북 기반에서는 사용시 오류가 발생한다. 따라서 matplotlib를 사용해서 주피터 노트북 기반 이미지 시각화를 해야한다.
- cv2.imshow() : 윈도우 frame에 보여줌(따라서 주피터에서는 안보인다.)
- cv2.waitKey() : 키보드 입력이 있을때 까지 무한 대기
- cv2.destroyAllWindows() : 화면의 윈도우 프레임 모두 종료
2. OpenCV의 이미지 처리
- Python에서 사용되는 여러 image라이브러리를 간단히 살펴보고 OpenCV와의 차이 이해
- OpenCV의 단일 이미지 처리 방식 이해
- OpenCV의 비디오 영상 처리 방식 이해
OpenCV 이미지 처리 이해 및 타 패키지 비교
PIL 패키지를 이용하여 이미지 로드하기
import matplotlib.pyplot as plt
%matplotlib inline
# https://korbillgates.tistory.com/85 - Jupyter Line에 그림 표등이 출력될 수 있게 하는 것
from PIL import Image
# PIL은 oepn()으로 image file을 읽어서 ImageFile객체로 생성.
pil_image = Image.open("../../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'>
skimage(사이킷이미지)로 이미지 로드 하기
- skimage는 imread()를 이용하여 RGB 원본 이미지를 RGB 형태의 넘파이 배열로 반환함.
from skimage import io
#skimage는 imread()를 이용하여 image를 numpy 배열로 반환함.
sk_image = io.imread("../../data/image/beatles01.jpg")
print('sk_image type:', type(sk_image), ' sk_image shape:', sk_image.shape)
plt.figure(figsize=(10, 10))
plt.imshow(sk_image)
#plt.show()
sk_image type: <class 'numpy.ndarray'> sk_image shape: (633, 806, 3)
OpenCV, matplotlib으로 이미지 로드하기
- OpenCV는 imread()를 이용하여 원본 RGB 이미지를 BGR 형태의 넘파이 배열로 반환함.
- OpenCV의 imwrite()를 이용한다면 BGR 형태의 이미지 배열을 파일에 기록할 때 다시 RGB형태로 변환하므로 사용자는 RGB->BGR->RGB 변환에 신경쓰지 않아도 됨.
import cv2
cv2_image = cv2.imread("../../data/image/beatles01.jpg") # cv2_image 여기서는 BGR이지만
cv2.imwrite("../../data/output/beatles02_cv.jpg", cv2_image) # 여기서는 RGB로 자동으로 다시 바뀐다
print('cv_image type:', type(cv2_image), ' cv_image shape:', cv2_image.shape)
plt.figure(figsize=(10, 10))
img = plt.imread("../../data/output/beatles02_cv.jpg")
plt.imshow(img)
#plt.show()
cv_image type: <class 'numpy.ndarray'> cv_image shape: (633, 806, 3)
OpenCV의 imread()로 반환된 BGR 이미지 넘파이 배열을 그대로 시각화 하기
- OpenCV의 imread()는 RGB를 BGR로 변환하므로 원하지 않는 이미지가 출력됨
cv2_image = cv2.imread("../../data/image/beatles01.jpg")
plt.figure(figsize=(10, 10))
plt.imshow(cv2_image)
plt.show()
sk_image = io.imread("../../data/image/beatles01.jpg")
print(sk_image.shape)
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("../../data/image/beatles01.jpg")
print(type(cv2_image))
print(cv2_image.shape)
cv2_image[:, :, 0] # BGR - R값만 print한다. (633, 806, 3) -> (633, 806, 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] # BRG - G값만 print한다. (633, 806, 3) -> (633, 806, 0)
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("../../data/image/beatles01.jpg")
draw_image = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2RGB) # 이 작업에 시간 많이 안든다. 단순 메모리 포인트 변화만 하므로.
cv2.imwrite("../../data/output/beatles02_cv.jpg", draw_image)
# 이렇게 저장된 것을 확인해보면 색이 엉망인것을 확인할 수 있다. 따라서 imwirte를 사용해서 저장한다면 반드시 BGR 이미지가 들어가게 하도록!
plt.figure(figsize=(10, 10))
plt.imshow(draw_image)
plt.show()
3. OpenCV 영상처리
- 뼈대 코드 이다. 나중에 Object Detection 결과를 동영상으로 저장할 때 이와 같은 방법을 사용할 것이므로, 꼭 잘 알아두어야 한다.
- OpenCV는 간편하게 비디오 영상처리를 할 수 있는 API를 제공
- VideoCapture 객체는 Video Streaming을 Frame 별로 Capture하여 처리할 수 있는 기능 제공
- VideoWriter 객체는 VideoCapture로 읽어들인 Frame을 동영상으로 Write하는 기능 제공
# 일단 시각화만 해본다.
# wget https://github.com/chulminkw/DLCV/blob/master/data/video/Night_Day_Chase.mp4?raw=true 으로 다운로드 가능.
from IPython.display import clear_output, Image, display, Video, HTML
Video('../../data/video/Night_Day_Chase.mp4') # 소리도 있음
- 여기서부터 Video를 사용하는 방법이다.
- Linux에서 video output의 확장자는 반드시 avi 로 설정 필요. (mp4로 재생되긴 되더라..)
OpenCV 영상 처리 개요
- VideoCapture클래스는 동영상을 개별 Frame으로 하나씩 읽어(wideoCapture.read()) 들이는 기능을 제공한다.
- VideoWriter는 VideoCapture로 읽어들인 개별 Frame을 차곡차곡 쌓아서 Write 수행하며 하나의 동영상을 만든다.
- get 메소드를 사용해서 동영상으 상세 정보를 하나하나 불러들여올 수 있다.
VideoWriter(저장위치, 코덱, 프레임, 사이즈(1x2 배열))
- 코덱 포멧을 다양하게 Encoding할 수 있다. (DIVX, XVID, MJPG, X264, WMV1, WMV2)
- 하지만 가상환경의 우리의 리눅스 환경에서는 XVID 코덱을 사용해야 하고, write시 동영상 인코더는 avi로 해야한다. input을 mp4를 했다고 하더라도. ㅈ
# 여기서부터 Video를 사용하는 방법이다.
import cv2
video_input_path = '../../data/video/Night_Day_Chase.mp4'
# linux에서 video output의 확장자는 반드시 avi 로 설정 필요.
video_output_path = '../../data/output/Night_Day_Chase_output.avi'
cap = cv2.VideoCapture(video_input_path) # <- 앞으로 이 변수를 중심으로 사용할 것
# Codec은 *'XVID'로 설정.
codec = cv2.VideoWriter_fourcc(*'XVID') # 코덱 객체를 생성
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 )
frame_cnt = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
vid_writer = cv2.VideoWriter(video_output_path, codec, vid_fps, vid_size) # 처음에는 아무것도 없는 동영상
print('총 Frame 갯수:', frame_cnt, 'FPS:', round(vid_fps), 'Frame 크기:', vid_size)
총 Frame 갯수: 1383 FPS: 28 Frame 크기: (1216, 516)
import time
green_color=(0, 255, 0)
red_color=(0, 0, 255)
start = time.time()
index=0
while True:
hasFrame, img_frame = cap.read()
# cap.read가 frame 하나하나를 순서대로 뱉어준다. 한 프래임씩 읽어주는 아주 좋은 매소드함수라고 할 수 있다.
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) # 동영상 프레임 이미지 각각에 몇번째 Frame인지 적기
vid_writer.write(img_frame)
# 동영상에 차곡차곡 동영상프래임이미지를 쌓아올린다.
print('write 완료 시간:', round(time.time()-start,4))
vid_writer.release() # 나중에 저장한 동영상. file.close() 와 같은 느낌
cap.release() # 처음에 가져온 이미지. file.close() 와 같은 느낌
frame : 1 처리 완료
frame : 2 처리 완료
frame : 3 처리 완료
frame : 4 처리 완료
fr...
frame : 1381 처리 완료
frame : 1382 처리 완료
frame : 1383 처리 완료
더 이상 처리할 frame이 없습니다.
write 완료 시간: 12.3946
Video('../../data/output/Night_Day_Chase_output.avi')
- avi 파일은 주피터 노트북에서 실행할 수 없다. 그래서 파일을 만들었으면, 위와 같이 실행할 수 없다. mp4 파일만 실행할 수 있음을 알아두자.
- 그래서 avi 파일은 다운을 받아서 나의 컴퓨터에 실행해봐야 한다. 다운을 받는 방법은 그냥 jupyter notebook에서 download를 하면 된다. 그리고 아래와 같이 object storage에 넣고, 다운 받는 방법 또한 있다.
- 당연히 동영상에 소리는 없다. Detection을 위한다면 소리는 필요없다.
!gsutil cp ../../data/output/Night_Day_Chase_output.avi gs://my_bucket_dlcv/data/output/Night_Day_Chase_output.avi