안녕하세요. 지난 포스팅의 Opencv 제대로 쓰기[0]에서는 환경설정 및 간단한 테스트를 해보았습니다. 오늘은 본격적으로 opencv를 다루어보도록 하겠습니다. 가장 기본이라고 할 수 있는 영상 및 동영상을 불러오고 저장하는 방법에 대해서 알아보도록 하겠습니다.
1. 컴퓨터에 있는 영상 불러오기 및 저장하기
컴퓨터에 있는 영상을 불러오는 것은 지난 포스팅에서 간단하게 테스트 해보았듯이 기본적으로 cv2.imread(fileName, flag)가 기본입니다. 각 입력 파라미터는 아래와 같습니다.
- fileName (str): 영상 경로
- flag (int): 영상을 불러올 때 옵션
- Return : 영상 객체
- Return Type : numpy.ndarray
여기서 flag 옵션은 다시 한번 3개로 나뉘게 됩니다.
- cv2.IMREAD_COLOR (1) : 영상 파일을 Color로 입력
- cv2.IMREAD_GRAYSCALE (0) : 영상 파일을 Gray Scale로 입력
- cv2.IMREAD_UNCHANGED (-1) : 영상 파일을 alpha channel(투명도 채널)을 포함하여 입력
각 옵션은 옆에 붙은 0, 1, -1의 정수로 대체할 수 있습니다. 그리고 영상을 불러오는 것을 실패하면 위 함수는 None을 반환해줍니다. 불러오는 것을 성공한다면 영상 객체가 반환됩니다. 그리고 그 타입은 numpy.ndarray이죠. 즉, 넘파이 라이브러리와 opencv는 뗄레야 뗄수없는 각별한 사이가 될 수 밖에 없습니다. 만약 넘파이 라이브러리에 대해서 아직 잘 모르신다면 제가 이전에 정리한 "넘파이 알고 쓰자" 시리즈를 틈틈히 보시는 것을 추천드립니다.(블로그 검색 창 : 넘파이 알고 쓰자를 입력하시면 됩니다.) 넘파이 배열의 특성을 아신다면 중요한 속성이 한가지 존재합니다. 바로 shape이죠. 이 부분은 넘파이 배열을 공부하시면 알 수 있기 때문에 설명은 생략하도록 하겠습니다.
하지만 영상을 불러오기만 하면 아무것도 할 수 없습니다. 일단 사용자에게 띄워주어야겠죠? 이때 사용할 수 있는 함수가 cv2.imshow( title, image)입니다.
- title (str): 윈도우 창의 제목
- image (numpy.ndarray): cv2.imread()의 반환값
그리고 이 함수는 창을 하나 띄워서 입력된 영상을 보여줍니다. 여기서 주의할 점이 한 가지 있습니다. 바로 cv2.imshow 함수는 항상 함께 쓰이는 함수 2개가 존재합니다.
- cv2.waitKey() : 키보드 입력을 대기하는 함수로 "0"을 입력하면 입력이 들어올때까지 무한 대기, 0이 아닌 정수를 입력하면 해당 시간동안 milisecond를 대기
- cv2.destroyAllWindows() : 화면에 나타나는 원도우를 종료
이를 이용해서 간단하게 띄운 것이 지난 포스팅의 코드입니다.
import cv2
# print("opencv-python version = ", cv2.__version__)
img = cv2.imread('./example/Lenna.png', cv2.IMREAD_COLOR) # Color Image Load
img = cv2.imread('./example/Lenna.png', cv2.IMREAD_GRAYSCALE) # Grayscale Image Load
print(img.shape)
cv2.imshow("Lenna", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
지금은 어떠한 영상 처리도 적용하지 않았지만 어떠한 디지털 영상 처리(블러링, 샤프닝, 노이즈 제거 등등)을 했다고 가정하면 그 결과를 저장해야겠죠? 이때, 사용할 수 있는 함수가 cv2.imwrite(fileName, image)입니다.
- fileName (str) : 저장될 파일명
- image (numpy.ndarray): 저장할 이미지
cv2.imwrite('result.png', img)
2. 컴퓨터에 있는 동영상 불러오기 및 저장하기
지금까지 본 것은 영상(image)를 불러온 것입니다. 이번에는 동영상(video)를 불러오도록 하겠습니다. 사실 동영상을 불러오는 것도 영상과 크게 차이가 없기 때문에 쉽게 이해하실 수 있습니다. 기본적으로 필요한 함수는 cv2.VideoCapture()입니다. 그리고 그 과정은 아래와 같습니다.
- cv2.VideoCapture() 함수를 통해 VideoCapture Object를 생성합니다. 변수로 camera device index 또는 동영상 파일명을 넘겨줍니다. 만약 0으로 설정하면 자동으로 기본 장치의 카메라로 연결됩니다.
- 반복문을 계속 돌면서 한 프레임씩 읽어옵니다.
- 읽은 프레임에 대해서 변환작업을 수행하고 화면에 보여줍니다.
- 영상 재생이 끝나면 VideoCapture Object를 release하고 window를 닫습니다.
저희는 지금 파일로부터 동영상을 읽어올것이기 때문에 cv2.VideoCapture()에 파일명을 넘겨서 VideoCapture Object를 생성해주면 됩니다. 이때, 옳지않은 파일 경로라면 아예 오류를 띄우기 때문에 꼭 예외처리를 해주시길 바랍니다. 이제 어떤 변환을 적용해서 변환된 동영상을 저장할려고 한다면 cv2.VideoWriter(outputFile, fourcc, frame, size)를 사용하면 됩니다.
- outputFile (str) : 저장될 파일명
- fourcc : Codec 정보, cv2.VideoWriter_fourcc()
- frame (float) : 초당 저장될 프레임
- size (list) : 저장될 사이즈
예시 동영상은 제가 직접 찍은 것으로 해보도록 하겠습니다.
목표는 위 동영상을 수평으로 뒤집어서 저장하는 것입니다.
import cv2
cap = cv2.VideoCapture('./example/sample_video.MOV')
width = int(cap.get(3)) # 가로 길이 가져오기
height = int(cap.get(4)) # 세로 길이 가져오기
fps = 20
fcc = cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')
out = cv2.VideoWriter('output.avi', fcc, fps, (width, height))
while (cap.isOpened()) :
ret, frame = cap.read()
if ret :
frame = cv2.flip(frame, 0)
out.write(frame)
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q') : break
else :
print("Fail to read frame!")
break
cap.release()
out.release()
cv2.destroyAllWindows()
확실히 영상을 처리하는 것보다 코드는 조금 더 길어졌지만 그래도 중요한 부분이기 때문에 분석해보도록 하겠습니다. 제 노트북은 현재 맥북이기 때문에 cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')로 썻지만 만약 OS가 윈도우이신 분들은 cv2.VideoWriter_fourcc('D', 'I', 'V', 'X')로 codec을 넣어주셔야합니다. 그리고 cv2.VideoWriter('output.avi', fcc, fps, (width, height))에서는 각각 불러올 영상의 경로, codec 정보, 저장할 초당 프레임(frame per second; fps), 그리고 저장할 동영상의 사이즈를 넘겨줍니다. 그러면 cv2.VideoCapture()에 파일명을 넘겨서 VideoCapture Object를 생성하는 것과 마찬가지로 VideoWriter Object인 out을 생성해주게 됩니다.
그 다음에는 while 문으로 들어가게 됩니다. 기본적으로 비디오를 불러오는 과정은 while을 쓰는 것이 흔한데, 그 이유는 이와 같이 컴퓨터내에 저장된 동영상이 아닌 웹캠으로 입력받는 다고 쳤을 때 언제 입력이 종료될지 모르기 때문에 while을 쓰게 됩니다. while의 조건문에 VideoCapture Object의 내장함수인 cap.isOpened()가 조건으로 들어가있습니다. isOpened()함수는 cap이 제대로 초기화가 되었는 지 확인하는 함수입니다. 만약, 초기화가 되지 않는다면 cap.open()이라는 함수를 이용하면 됩니다.
while에서 첫번째로 만나는 명령어는 ret, frame = cap.read()입니다. cap.read() 함수는 입력된 동영상의 프레임을 하나씩 읽어옵니다. 만약 프레임을 정상적으로 읽어왔다면 ret는 True, 읽어오지 않았다면 False를 반환합니다. 그래서 while문의 탈출조건은 ret로 제대로 읽지 않은 경우는 더 이상 읽을 프레임이 없는 경우라고 볼 수 있기 때문이죠. 그리고 읽어온 프레임을 cv2.flip() 함수를 이용해서 위 아래를 뒤집은 뒤 out.write(frame)으로 해당 프레임을 저장합니다.
모든 과정이 끝나면 정상적으로 끝내기 위해서 release()를 이용해서 생성된 각 객체를 메모리에서 해제해줍니다. 만약, 정상적이라면 위의 동영상의 위아래가 뒤집히게 됩니다.
참고자료
[1].opencv-python.readthedocs.io/en/latest/doc/01.imageStart/imageStart.html
[2].opencv-python.readthedocs.io/en/latest/doc/02.videoStart/videoStart.html#camera
'Programming > Python' 카테고리의 다른 글
Opencv 제대로 쓰기[3].외부 카메라 동영상 스크린샷 저장하기 (0) | 2021.05.01 |
---|---|
Opencv 제대로 쓰기[2]- 외부 카메라로부터 동영상 입력받기 (0) | 2021.04.25 |
Opencv 제대로 쓰기[0] - Opencv-python 설치 및 환경설정 (1) | 2021.04.11 |
Sympy 알고 쓰자 - polynomial Simplify (0) | 2020.12.31 |
Sympy 알고 쓰자 - Common Simplification (0) | 2020.12.10 |