OpenCV 프로그래밍 - Chapter 02 영상 및 비디오 입출력

업데이트:

카테고리:

태그:

이 포스팅은 ‘python으로 배우는 OpenCV 프로그래밍’ 교재를 참고하여 작성하였습니다.

01 영상 입출력과 디스플레이

파이썬에서 OpenCV로 영상파일을 읽고, 윈도우 화면에 표시하고, 파일로 저장하는 방법에 대해 알아본다.
아래 표현에서 [ ]는 생략 가능한 옵션을 나타내고, 화살표는 함수의 반환값을 표현한다.
반환값이 없을 경우 화살표는 생략한다.

영상 입출력과 디스플레이 함수

함수 비고
cv2.imread(filename[,flags]) -> retval 영상 입력
cv2.imwrite(filename,img[,params]) -> retval 영상파일 출력
cv2.namedWindow(winname[,flags]) 윈도우 생성
cv2.imshow(winname, mat) 윈도우 표시
cv2.waitKey([, delay]) -> retval 키보드 입력 대기
cv2.destroyWindow(winname) 윈도우 파괴
cv2.destroyAllWindows() 모든 윈도우 파괴
  • 각 메서드에 대한 설명
    1. cv2.imread()는 영상파일을 numpy.ndarray의 배열로 읽어 반환한다.
      읽기에 실패하면 None을 반환하며, flags는 cv2.IMREAD_COLOR(디폴트), cv2.IMREAD_GRAYSCALE, cv2.IMREAD_UNCHANGED등의 읽기 옵션이다.
    2. cv2.imwrite()는 numpy.ndarray의 배열 img를 filename의 영상파일로 저장한다.
      파일의 확장자에 의해 영상포맷이 결정된다.
      params는 압축 관련 인수로 생략할 수 있다.
    3. cv2.namedWindow()는 지정한 winname에 해당하는 이름을 갖는 윈도우를 생성한다.
      flags는 cv2.WINDOW_AUTOSIZE(디폴트), cv2.WINDOW_NORMAL이 있다.
    4. cv2.imshow()는 winname윈도우에 영상 mat를 표시한다.
      winname 윈도우가 생성되어 있지 않아도 실행가능하다.
    5. cv2.waitKey()는 키보드 입력을 위해 ms단위로 대기한다. 디폴트값은 0이며 디폴트일땐 키보드 입력이 있을 때까지 무한대기한다.
    6. cv2.destroyWindow()는 주어진 winname윈도우만 파괴하고, cv2.destroyAllWindows()는 모든 윈도우를 파괴한다.

영상파일 읽기 및 화면 표시

import cv2

imageFile = './data/lena.jpg'
img  = cv2.imread(imageFile)    # cv2.IMREAD_COLOR
img2 = cv2.imread(imageFile, 0) # cv2.IMREAD_GRAYSCALE
cv2.imshow('Lena color',img)
cv2.imshow('Lena grayscale',img2)

cv2.waitKey()
cv2.destroyAllWindows()

print(img.shape)
print(img2.shape)

위 실습코드를 실행 시키면 다음과 같은 결과가 출력되며, 키보드 입력이 있으면 모든 윈도우가 파괴된다.
image

또한 위 코드를 실행하면 다음과 같은 결과가 출력된다.

(512, 512, 3)
(512, 512)

이 결과가 의미하는 바는 img = cv2.imread(imageFile)는 디폴트로 BGR 3채널의 값으로 출력됨을 알 수 있다.
그리고 img2 = cv2.imread(imageFile, 0)는 flags값에 cv2.IMREAD_GRAYSCALE를 의미하는 0값이 입력되어 있으므로 1채널의 값으로 출력됨을 알 수 있다.
또한 이미지의 픽셀이 512*512라는 것 또한 알 수 있다.

영상파일 저장

import cv2

imageFile = './data/lena.jpg'
img = cv2.imread(imageFile) # cv2.imread(imageFile, cv2.IMREAD_COLOR)
cv2.imwrite('./data/Lena.bmp', img)
cv2.imwrite('./data/Lena.png', img)
cv2.imwrite('./data/Lena2.png',img, [cv2.IMWRITE_PNG_COMPRESSION, 9])
cv2.imwrite('./data/Lena2.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 90])

위 실습 코드는 영상파일을 저장하는 코드이며 간단한 예제이므로 설명은 생략한다.

matplotlib 1 : 컬러 영상 표시

import cv2
from   matplotlib import pyplot as plt

imageFile = './data/lena.jpg'
imgBGR = cv2.imread(imageFile) # cv2.IMREAD_COLOR
#plt.axis('off')
plt.imshow(imgBGR)
plt.show()

imgRGB = cv2.cvtColor(imgBGR,cv2.COLOR_BGR2RGB)
plt.imshow(imgRGB)
plt.show()

matplotlib은 RGB가 디폴트고, cv2는 BGR이 디폴트이므로 plt.show()를 사용해 결과를 출력할때에는 cv2.cvtColor()를 사용해서 BGR을 RGB로 변환해줘야 한다.
변환하지 않고 그대로 출력할 경우 다음과 같고
image

변환하고 출력할 경우 다음과 같다.

image

각 출력결과의 하단에 표시된 값을 비교하면 이해하기 쉽다. 즉 채널을 표현하는 순서가 다를 뿐 RGB로 처리하는 것은 동일하다.
matplotlib는 RGB순서로 RGB를 표현하고, cv2는 BGR순서로 RGB를 표현한다.

matplotlib 2 : 그레이스케일 영상 표시

import cv2
from   matplotlib import pyplot as plt

imageFile = './data/lena.jpg'
imgGray = cv2.imread(imageFile, cv2.IMREAD_GRAYSCALE)
#plt.axis('off')

plt.imshow(imgGray, cmap = "gray", interpolation='bicubic')
plt.show()

plt.imshow(imgGray, cmap = "gray", interpolation='bicubic')는 imgGray영상을 ‘gray’컬러맵(cmap), ‘bicubic’으로 보간한다.
따라서 다음과 같은 결과가 출력되며 3개의 채널을 가지던 RGB와 달리 1개의 채널만 가지고 있음을 알 수 있다. image

만약 plt.imshow(imgGray)로 결과를 출력한다면 다음과 같은 결과가 출력된다.

image

실행 시켜본 결과 0이 검정색 255가 흰색을 의미하는 것을 알 수 있었다.

위에서 plt로 결과 출력한 윈도우에는 여백이 존재한다.
다음 코드는 이 여백을 조정하는 코드이다.

matplotlib3 : 여백 조정 및 영상 저장

import cv2
from   matplotlib import pyplot as plt

imageFile = './data/lena.jpg'
imgGray = cv2.imread(imageFile, cv2.IMREAD_GRAYSCALE)

plt.figure(figsize=(6,6))
plt.subplots_adjust(left=0, right=1, bottom=0, top=1)
plt.imshow(imgGray, cmap = 'gray')
##plt.axis('tight')
plt.axis('off')
plt.savefig('./data/0205.png')
plt.show()

plt.figure(figsize = (6, 6))은 크기를 가로,세로 6인치로 설정한다.
plt.subplots_adjust(left=0, right=1, bottom=0, top=1)는 영상 출력 범위를 좌우 [0, 1], 상하 [0, 1]로 비율을 여백 없이 채운다.
실행 결과는 다음과 같다.

image

matplotlib4 : 서브플롯에 영상 표시

import cv2
from   matplotlib import pyplot as plt

path = './data/'
imgBGR1 = cv2.imread(path+'lena.jpg')
imgBGR2 = cv2.imread(path+'apple.jpg')
imgBGR3 = cv2.imread(path+'baboon.jpg')
imgBGR4 = cv2.imread(path+'orange.jpg')

# 컬러 변환: BGR -> RGB
imgRGB1 = cv2.cvtColor(imgBGR1, cv2.COLOR_BGR2RGB)
imgRGB2 = cv2.cvtColor(imgBGR2, cv2.COLOR_BGR2RGB)
imgRGB3 = cv2.cvtColor(imgBGR3, cv2.COLOR_BGR2RGB)
imgRGB4 = cv2.cvtColor(imgBGR4, cv2.COLOR_BGR2RGB)

fig, ax = plt.subplots(2, 2, figsize=(6,6), sharey=True)
fig.canvas.set_window_title('Sample Pictures')

ax[0][0].axis('off')
ax[0][0].imshow(imgRGB1, aspect = 'auto')

ax[0][1].axis('off')
ax[0][1].imshow(imgRGB2, aspect = 'auto')

ax[1][0].axis("off")
ax[1][0].imshow(imgRGB3, aspect = "auto")

ax[1][0].axis("off")
ax[1][1].imshow(imgRGB4, aspect = 'auto')

plt.subplots_adjust(left=0, bottom=0, right=1, top=1,
                    wspace=0.05, hspace=0.05)
plt.savefig("./data/0206.png", bbox_inches='tight')
plt.show()

앞서 설명한 바와 같이 cv2에서 BGR로 받은 채널값을 plt에서 RGB로 표현하기 위해 cv2.cvtColor()를 사용하고 추후에 서브플롯을 사용 할 일이 있으면 예제코드를 참고하도록 한다.

image

02 비디오 프레임 캡처와 화면 표시

비디오 프레임 캡처와 화면 표시 함수

함수 비고
cv2.VideoCapture() -> VideoCapture object
cv2.VideoCapture(filename) -> VideoCapture object
cv2.VideoCapture(device) -> VideoCapture object
비디오 획득 객체 생성
cv2.VideoCapture.read([image]) -> retval, image 프레임 획득
cv2.VideoCapture.grab() -> retval 프레임 잡기
cv2.VideoCapture.retrieve([image[, channel]]) -> retval, image 프레임 획득
cv2.VideoCapture.release() 비디오 획득 객체 해제
cv2.VideoCapture.get(propld) -> retval 비디오 특성 얻기
cv2.VideoCapture.set(propld, value) -> retval 비디오 특성 설정
  • 각 메서드에 대한 설명
    1. cv2.VideoCapture()는 비디오 파일 filename 또는 카메라 번호 device로부터 VideoCapture 객체를 생성하여 반환한다.
      장치번호는 내장웹캠의 경우 0이고, 외부 캠은 1이다.
      또한 VideoCapture.isOpened()를 사용하면 비디오 객체가 개방되었는지 확인할 수 있다.
    2. cv2.VideoCapture()는 개방된 VideoCapture 객체로부터 다음 비디오 프레임을 grab하고 디코딩해서 프레임을 반환한다.
      VideoCapture.grab()cv2.VideoCapture.retrieve()를 모두 수행한 결과로 비디오 프레임 캡처를 위해 자주 사용한다.
      프레임 캡쳐에 성공할 경우 True 실패할 경우 False를 반환한다.
    3. cv2.VideoCapture.grab()은 개방된 VideoCapture 객체에서 다음 프레임을 grab하기 위해 사용한다.
    4. cv2.VideoCapture.retrieve()cv2.VideoCapture.grab()에 의해 잡힌 영상을 디코딩하여 image로 반환한다.
      프레임 캡쳐에 성공할 경우 True 실패할 경우 False를 반환한다.
    5. cv2.VideoCapture.get()은 개방된 VideoCapture 객체의 특성을 실수로 반환한다.
      객체의 특성인 property_id는 아래의 표와 같다.
    6. cv2.VideoCapture.set()은 개방된 VideoCapture 객체의 propld 특성을 value로 설정한다.
      설정의 성공 여부를 True, False로 반환한다.
property_id 설명
cv2.CAP_PROP_POS_MSEC ms로 현재 위치
cv2.CAP_PROP_POS_FRAMES 캡처될 프레임 번호
cv2.CAP_PROP_FRAME_WIDTH 비디오 프레임의 가로 크기
cv2.CAP_PROP_FRAME_HEIGHY 비디오 프레임의 세로 크기
cv2.CAP_PROP_FPS 프레임 속도
cv2.CAP_PROP_FOURCC 코덱의 4 문자
cv2.CAP_PROP_FRAME_COUNT 비디오 파일에서 총 프레임 수
cv2.CAP_PROP_CONVERT_RGB 영상이 RGB로 변환해야 하는지 여부
cv2.CAP_PROP_FORMAT 캡쳐된 영상 포맷
cv2.CAP_PROP_BRIGHTNESS 카메라에서 영상의 밝기
cv2.CAP_PROP_CONTRAST 카메라에서 영상의 대비
cv2.CAP_PROP_SATURATION 카메라에서 영상의 포화도
cv2.CAP_PROP_HUE 카메라에서 영상의 채도
cv2.CAP_PROP_GAIN 카메라에서 영상의 Gain
cv2.CAP_PROP_EXPOSURE 카메라에서 영상의 노출
import cv2

##cap = cv2.VideoCapture(0)  # 0번 카메라
cap = cv2.VideoCapture('./data/vtest.avi')
##cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
##cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)

frame_size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
              int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
print('frame_size =', frame_size)

while True:   
    retval, frame = cap.read() # 프레임 캡처
    if not retval:
        break

    cv2.imshow('frame',frame)
    
    key = cv2.waitKey(25)
    if key == 27: # Esc
        break
if cap.isOpened():
    cap.release()
cv2.destroyAllWindows()

03 비디오 파일 녹화

비디오 파일 녹화 함수

함수 비고
cv2.VideoWriter([filename, fourcc, fps, frameSize[,isColor]]) -> VideoWriter object 비디오 출력 객체 생성
cv2.VideoWriter.write(image) 비디오 파일에 이미지 출력
cv2.VideoWriter.release() 비디오 출력 객체 해제
  • 각 메서드에 대한 설명
    1. cv2.VideoWriter()는 VideoWriter 객체를 생성하여 반환한다.
      filename은 비디오 파일의 이름이고 fourcc는 비디오 코덱을 위한 4-문자이다.
      주요 코덱 문자는 아래의 표와 같다.
      fps는 프레임 속도, frameSize는 프레임의 크기, isColor는 True일 경우 컬러 False일 경우 그레이스케일 비디오이다.
    2. cv2.VideoWriter.write()는 개방된 VideoWriter 객체에 image를 출력한다.
      주의할 것은 image의 크기는 VideoWriter 객체를 생성할 때 명시한 프레임 크기인 frameSize와 같아야 한다.
    3. cv2.VideoWriter.release()는 개방된 VideoWriter 객체를 해제한다.
fourcc 코덱
cv2.VideoWriter_fourcc(*‘PIM1’) MPEG-1
cv2.VideoWriter_fourcc(*‘MJPG’) Motion-JPEG
cv2.VideoWriter_fourcc(*‘DIVX’) DIVX4.0 이후 버전
cv2.VideoWriter_fourcc(*‘XVID’) XVID, MPEG-4
cv2.VideoWriter_fourcc(*‘MPEG’) MPEG
cv2.VideoWriter_fourcc(*‘X264’) H.264/AVC
import cv2

cap = cv2.VideoCapture(0) # 0번 카메라
frame_size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
              int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
print('frame_size =', frame_size)

#fourcc = cv2.VideoWriter_fourcc(*'DIVX')  # ('D', 'I', 'V', 'X')
fourcc = cv2.VideoWriter_fourcc(*'XVID')

# 객체 생성
out1 = cv2.VideoWriter('./data/record0.mp4',fourcc, 20.0, frame_size)
out2 = cv2.VideoWriter('./data/record1.mp4',fourcc, 20.0, frame_size, isColor=False)

while True:
    retval, frame = cap.read()
    if not retval:
        break   
    #객체1 출력
    out1.write(frame)
    
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    #객체2 출력
    out2.write(gray)
    
    #객체1, 객체2 윈도우에 표시      
    cv2.imshow('frame',frame)
    cv2.imshow('gray',gray)      
    
    key = cv2.waitKey(25)
    if key == 27:
        break
        
# 객체 해제
cap.release()
out1.release()
out2.release()

# 윈도우 파괴
cv2.destroyAllWindows()

cv2.imshow('frame',frame)cv2.imshow('gray',gray)를 하지 않으면 영상이 저장되지 않는다.

댓글남기기