OpenCV 프로그래밍 - Chapter 02 영상 및 비디오 입출력
카테고리: OpenCV
이 포스팅은 ‘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() | 모든 윈도우 파괴 |
- 각 메서드에 대한 설명
- cv2.imread()는 영상파일을 numpy.ndarray의 배열로 읽어 반환한다.
읽기에 실패하면 None을 반환하며, flags는cv2.IMREAD_COLOR(디폴트)
,cv2.IMREAD_GRAYSCALE
,cv2.IMREAD_UNCHANGED
등의 읽기 옵션이다. - cv2.imwrite()는 numpy.ndarray의 배열 img를 filename의 영상파일로 저장한다.
파일의 확장자에 의해 영상포맷이 결정된다.
params는 압축 관련 인수로 생략할 수 있다. - cv2.namedWindow()는 지정한 winname에 해당하는 이름을 갖는 윈도우를 생성한다.
flags는cv2.WINDOW_AUTOSIZE(디폴트)
,cv2.WINDOW_NORMAL
이 있다. - cv2.imshow()는 winname윈도우에 영상 mat를 표시한다.
winname 윈도우가 생성되어 있지 않아도 실행가능하다. - cv2.waitKey()는 키보드 입력을 위해 ms단위로 대기한다. 디폴트값은 0이며 디폴트일땐 키보드 입력이 있을 때까지 무한대기한다.
- cv2.destroyWindow()는 주어진 winname윈도우만 파괴하고, cv2.destroyAllWindows()는 모든 윈도우를 파괴한다.
- cv2.imread()는 영상파일을 numpy.ndarray의 배열로 읽어 반환한다.
영상파일 읽기 및 화면 표시
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)
위 실습코드를 실행 시키면 다음과 같은 결과가 출력되며, 키보드 입력이 있으면 모든 윈도우가 파괴된다.
또한 위 코드를 실행하면 다음과 같은 결과가 출력된다.
(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로 변환해줘야 한다.
변환하지 않고 그대로 출력할 경우 다음과 같고
변환하고 출력할 경우 다음과 같다.
각 출력결과의 하단에 표시된 값을 비교하면 이해하기 쉽다.
즉 채널을 표현하는 순서가 다를 뿐 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개의 채널만 가지고 있음을 알 수 있다.
만약 plt.imshow(imgGray)
로 결과를 출력한다면 다음과 같은 결과가 출력된다.
실행 시켜본 결과 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]로 비율을 여백 없이 채운다.
실행 결과는 다음과 같다.
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()
를 사용하고 추후에 서브플롯을 사용 할 일이 있으면 예제코드를 참고하도록 한다.
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 | 비디오 특성 설정 |
- 각 메서드에 대한 설명
cv2.VideoCapture()
는 비디오 파일 filename 또는 카메라 번호 device로부터 VideoCapture 객체를 생성하여 반환한다.
장치번호는 내장웹캠의 경우 0이고, 외부 캠은 1이다.
또한VideoCapture.isOpened()
를 사용하면 비디오 객체가 개방되었는지 확인할 수 있다.cv2.VideoCapture()
는 개방된 VideoCapture 객체로부터 다음 비디오 프레임을 grab하고 디코딩해서 프레임을 반환한다.
VideoCapture.grab()
과cv2.VideoCapture.retrieve()
를 모두 수행한 결과로 비디오 프레임 캡처를 위해 자주 사용한다.
프레임 캡쳐에 성공할 경우 True 실패할 경우 False를 반환한다.cv2.VideoCapture.grab()
은 개방된 VideoCapture 객체에서 다음 프레임을 grab하기 위해 사용한다.cv2.VideoCapture.retrieve()
는cv2.VideoCapture.grab()
에 의해 잡힌 영상을 디코딩하여 image로 반환한다.
프레임 캡쳐에 성공할 경우 True 실패할 경우 False를 반환한다.cv2.VideoCapture.get()
은 개방된 VideoCapture 객체의 특성을 실수로 반환한다.
객체의 특성인 property_id는 아래의 표와 같다.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() | 비디오 출력 객체 해제 |
- 각 메서드에 대한 설명
cv2.VideoWriter()
는 VideoWriter 객체를 생성하여 반환한다.
filename은 비디오 파일의 이름이고 fourcc는 비디오 코덱을 위한 4-문자이다.
주요 코덱 문자는 아래의 표와 같다.
fps는 프레임 속도, frameSize는 프레임의 크기, isColor는 True일 경우 컬러 False일 경우 그레이스케일 비디오이다.cv2.VideoWriter.write()
는 개방된 VideoWriter 객체에 image를 출력한다.
주의할 것은 image의 크기는 VideoWriter 객체를 생성할 때 명시한 프레임 크기인 frameSize와 같아야 한다.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)
를 하지 않으면 영상이 저장되지 않는다.
댓글남기기