
1. OpenCV(Open Source Computer Vision Library)
컴퓨터 비전과 이미지 처리를 위한 오픈소스 라이브러리 (컴퓨터 비전 =/= OpenCV)
1999년 Intel에서 영상처리 기술을 개발하기 위한 목적으로 만들어짐
2000년 BSD 라이센스로 배포
2011년 이후 OpenCV2로 개발 시작 (현재 버전은 OpenCV4이지만, 지금까지 불리는 명칭은 OpenCV2임)
OpenCV 설치
pip install opencv-python (cv2라는 라이브러리가 따로 있기 때문에, 확실하게 숙지)
2. 컴퓨터 비전(Computer Vision)
컴퓨터를 이용해서 디지털 이미지나 비디오에서 정보를 추출하고 해석하는 기술과 분야
이미지 처리, 객체 탐지, 패턴 인식, 광학 문자 인식 등이 존재
3. 영상(image)
움직이는 것을 의미하는 것이 아님. 움직이는 것은 '동영상'
픽셀(pixel): 이미지를 구성하는 가장 작은 단위
바둑판 모양의 격자에 나열되어있는 형태, 2차원 행렬로 보통 표현됨.
영상 형태
1) 그레이스케일 영상
흑백사진처럼 색상 정보가 없는 영상.
밝기 정보만으로 구성된 영상. (밝기 정보는 256단계로 0~255까지 표현. 0이 검은색, 255가 흰색)
numpy.uint8 = 8bit = 1byte
(u- : unsigned의 의미. 8비트 부호는 기본적으로 -128~127까지의 숫자를 표현할 수 있는데,
unsigned는 부호없는정수를 의미하여 0~255까지를 나타냄)
가로크기 * 세로크기 = 28 * 28 = 784byte의 용량을 가짐
2) 트루컬러 영상
컬러사진처럼 색상 정보를 가지고 있기 때문에, 다양한 색상을 표현할 수 있는 영상.
red, green, blue 색 성분을 사용하고, 각 256단계로 표현.
픽셀의 표현 -> 튜플 형태로 (255, 255, 255)
numpy.ndarray = 3byte(red, green, blue 3색이 겹쳐있으므로)
가로크기 * 세로크기 * 3 = 28 * 28 * 3 = 2352byte의 용량을 가짐
영상 파일 형식
1) bmp
픽셀 데이터를 압축하지 않고 그대로 저장
용량이 매우 큼(픽셀의 일반적인 용량)
파일구조가 단순하여 별도의 라이브러리 없이 프로그래밍이 가능.
2) jpg, jpeg
압축률이 좋아서 파일 용량이 크게 감소
사진과 같은 컬러영상을 저장할 때 사용
손실 압축
3) gif
움직이는 영상을 지원
256색 이하의 영상을 저장
무손실 압축
4) png
웹 이미지용으로 권장
무손실, 손실 둘 다 압축 가능
알파채널(투명도)의 지원이 가능 (예: 255, 255, 255, 1)
3-1. OpenCV
import cv2
print('현재 opencv 버전: ', cv2.__version__)
img = cv2.imread('./dog.bmp') # 아무 옵션을 주지 않음: 컬러 영상으로 읽어옴
# cv2.IMREAD_GRAYSCALE: 그레이스케일 연산
cv2.imshow('img', img) # 창이름, 영상객체
cv2.waitKey() # 창이 계속 유지

3-2. matplotlib
import cv2
import matplotlib.pyplot as plt
# cv2를 이용하여 그레이스케일 표현
img_gray = cv2.imread('./dog.bmp', cv2.IMREAD_GRAYSCALE)
cv2.imshow('img_gray', img_gray)
cv2.waitKey()

# matplotlib를 이용하여 그레이스케일 표현
img_gray = cv2.imread('./dog.bmp', cv2.IMREAD_GRAYSCALE)
plt.axis('off') # 격자 제거
plt.imshow(img_gray, cmap='gray')
plt.show()

# matplotlib를 이용하여 트루컬러 표현
img_rgb = cv2.imread('./dog.bmp', cv2.IMREAD_COLOR) # IMREAD_COLOR는 기본값이므로 생략이 가능.
img_rgb = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR) # BGR을 RGB로 변환
plt.axis('off') # 격자 제거
plt.imshow(img_rgb) # 기본값이 BGR순이므로 21번 코드가 없는 경우 Blue가 출력
plt.show()

# matplotlib을 이용하여 흑백, 컬러 모두 표현
img_gray = cv2.imread('./dog.bmp', cv2.IMREAD_GRAYSCALE)
img_rgb = cv2.imread('./dog.bmp', cv2.IMREAD_COLOR)
img_rgb = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
plt.subplot(121) # 1행 2열 1번째 인덱스
plt.axis('off')
plt.imshow(img_gray, cmap='gray')
plt.subplot(122) # 1행 2열 2번째 인덱스
plt.axis('off')
plt.imshow(img_rgb)
plt.show()

3-3. image info
import cv2
# 이미지의 그레이스케일의 type, shape, dtype를 확인
img_gray = cv2.imread('./dog.bmp', cv2.IMREAD_GRAYSCALE)
print('img_gray type: ', type(img_gray)) # class numpy.ndarray
print('img_gray shape: ', img_gray.shape) # (364, 548) (OpenCV는 height, width 순서)
print('img_gray dtype: ', img_gray.dtype) # uint8
# 이미지의 트루컬러의 type, shape, dtype를 확인
img_color = cv2.imread('./dog.bmp')
print('img_color type: ', type(img_color)) # class numpy.ndarray
print('img_color shape: ', img_color.shape) # (364, 548, 3)
print('img_color dtype: ', img_color.dtype) # uint8

# img_color를 '가로*세로'의 형식으로 재출력하기
h, w = img_color.shape[:2] # shape로 뽑으면 튜플로 출력
print(f'img_color 사이즈: {w}*{h}')

# 그레이스케일과 컬러를 구별하는 방법
if len(img_gray.shape) == 2:
print('img_gray는 그레이스케일 영상입니다.')
elif len(img_gray.shape) == 3:
print('img_gray는 컬러 영상입니다.')

# img_color에 특정 색 정보로 영상을 출력하기 // BGR:(255,102,255)
import numpy as np
bgr_color = (255, 102, 255) # BGR(255, 102, 255) 색상 (핑크색)
img_color = np.full((364, 548, 3), bgr_color, dtype=np.uint8)
cv2.imshow('img_color', img_color)
cv2.waitKey()

3-4. create image
영상 출력
1) 그레이스케일 영상 출력
cv2.IMREAD_GRAYSCALE을 사용
img.shape: 차원의 크기. (height, weight)
2) 컬러 영상 출력
cv2.imread() 메소드로 불러온 영상의 색상 정보는 BGR 순서
matplotlib에서 출력하려면 RGB 순서로 변경해야 하기 때문에
cv2.cvtColor() 메소드를 사용하여 변경
img.shape: 차원의 크기. (height, weight, 3)
import cv2
import numpy as np
# 임의의 값을 넣은 영상
img1 = np.empty((240, 320), dtype=np.uint8)
cv2.imshow('img1', img1)
cv2.waitKey()

# 0을 넣은 컬러 영상
img2 = np.zeros((240, 320), dtype=np.uint8) # 0: black이므로 검은색의 화면이 생성
cv2.imshow('img2', img2)
cv2.waitKey()

# 1을 넣은 컬러 영상
img3 = np.ones((240, 320), dtype=np.uint8) * 120 # 밝기 정보에 120을 곱해줌 # 회색의 화면이 생성
cv2.imshow('img3', img3)
cv2.waitKey()

# 특정한 값을 넣은 컬러 영상
img4 = np.full((240, 320, 3), (255,102,255), dtype=np.uint8) # (255,102,255):pink
cv2.imshow('img4', img4)
cv2.waitKey()

3-5. copy image
import cv2
img_origin = cv2.imread('./dog.bmp')
# img_origin에서 일부만 잘라내고 싶은 경우. 동일한 메모리에 사용하는 방법
img_origin = img_origin[91:210, 125:245] # H, W
img_copy = img_origin
# img_copy를 잘라낸 사진으로 변경하고 싶은 경우. 메모리에 다른쪽으로 옮겨서 사용하는 방법
img_copy = img_origin[91:210, 125:245].copy()
cv2.imshow('img_origin', img_origin)
cv2.waitKey()

3-6. drawing
도형 그리기
1) 직선 그리기
cv2.line(영상, 직선의 시작점과 끝점(튜플로 저장), 선 색상, 선 두께, 선 타입)
2) 사각형 그리기
cv2.rectangle(영상, 사각형의 꼭지점 좌표(튜플로 저장), 선 색상, 선 두께)
// 선 두께가 '-1'인 경우, 사각형 내부를 색상으로 채움
3) 원 그리기
cv2.circle(영상, 원의 중앙 좌표(튜플로 저장), 반지름, 선 색상, 선 두께)
// 선 두께가 '-1'인 경우, 원 내부를 색상으로 채움
4) 문자열 출력하기
cv2.putText(영상, 문자열, 영상에서 문자열을 출력할 위치 좌표, 글꼴, 폰트 크기)
import cv2
import numpy as np
img = np.full((500, 500, 3), 255, np.uint8)
# 흰색 배경의 네모 박스 생성
cv2.line(img, (100, 70), (200, 70), (0, 0, 255), 5)
# 빨간색 선(두께5) 생성
cv2.rectangle(img, (50,200,150,100), (0, 255, 0), -1)
# 초록색 사각형 생성 # (50,200)의 가로 150 세로 100의 사각형 (x,y,w,h)
cv2.circle(img,(300, 100), 100, (255, 255, 0), -1)
# 하늘색 원 생성 # (300,100)의 중심점에서 반지름 100의 원
str = "Hello"
cv2.putText(img, str, (30, 350), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255))
# cv2.FONT_HERSHEY_SIMPLE: 글꼴 // 위치: (30, 350) 크기:2 색상 빨강
cv2.imshow('img', img)
cv2.waitKey()

3-7. camera in
(해당 파트는 웹캠이 없는경우 진행을 할 수 없음)
cv2.VideoCapture 클래스
- 카메라와 동영상으로부터 프레임(frame)을 받아오는 작업을 처리함
카메라 영상 입력
1) cv2.VideoCapture(index)
- index: 시스템의 기본 카메라를 open하려면 0 또는 카메라 고유의 값
2) cv2.VideoCapture.isOpened(): 카메라를 제대로 불러왔는지의 여부 확인
- True:성공, False:실패
3) cv2.CAP_PROP_FRAME_WIDTH: 카메라에서 읽어들인 가로사이즈를 확인
4) cv2.CAP_PROP_FRAME_HEIGHT: 카메라에서 읽어들인 세로사이즈를 확인
5) cv2.VideoCapture.read() : 영상을 하나씩 읽어들이는 방법
ret: 영상이 정상적으로 리턴되었는지 여부를 확인 ( True / False로 return됨)
frame: 영상(그레이스케일 영상 또는 컬러 영상 자체)
import sys
import cv2
cap = cv2.VideoCapture(0)
if not cap.isOpened(): # 카메라 영상을 open할 수 없는 경우
print('카메라를 열 수 없습니다')
sys.exit() # 프로그램 종료
print('카메라 연결 성공')
# 카메라의 가로, 세로 사이즈 불러오기
print('가로 사이즈: ', int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)))
print('세로 사이즈: ', int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
# 카메라 불러오기
while True:
ret, frame = cap.read()
if not ret:
break
cv2.imshow('frame', frame)
if cv2.waitKey(10)==27: # 10: 0.01초 / 27:ESC키. ESC키를 누르면 꺼짐
break
cap.release() # 메모리를 아낄 수 있음.
3-8. video in
동영상 입력
1) cv2.VideoCapture(파일명) // 파일명은 string으로 작성
나머지는 카메라 영상과 동일함
예제로 사용할 무료 동영상 다운로드 웹사이트: Pixabay https://pixabay.com/ko/videos

예제를 위해 가져온 영상: china.mp4
import cv2
import sys
cap = cv2.VideoCapture('./china.mp4')
if not cap.isOpened(): # 동영상을 open할 수 없는 경우
print('동영상을 열 수 없습니다')
sys.exit() # 프로그램 종료
# 동영상의 가로, 세로 사이즈, 프레임 수 불러오기
print('가로 사이즈:', int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)))
print('세로 사이즈:', int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
print('프레임 수:', int(cap.get(cv2.CAP_PROP_FRAME_COUNT)))
print('fps:', int(cap.get(cv2.CAP_PROP_FPS)))
while True:
ret, frame = cap.read()
if not ret: # return할 내용이 없다면, break
break
cv2.imshow('frame', frame)
if cv2.waitKey(10) == 27: # 0.01초마다 ESC버튼의 누름이 확인된다면, 창을 닫기
break
cap.release() # 메모리를 아낄 수 있음.

3-9. camera out
동영상 출력
1) FourCC(Four Character Code):
4byte로 된 문자열이며, data 형식을 구분하는 고유 글자
주로 AVI 파일의 영상 코덱을 구분할때 사용
Divx, Xvid, H264 ...
cv2.VideoWriter_fourcc(*'DIVX') // DIVX 포맷으로 저장
cv2.VideoWriter_fourcc(*'XVID') // XVID 포맷으로 저장
...
cv2.VideoWriter_fourcc(*'MP4V') // MP4V 포맷으로 저장
파일 저장
cv2.VideoWriter(파일명, FourCC객체, fps, 프레임사이즈, 컬러영상여부)
프레임사이즈: tuple형식(w, h)
컬러영상여부: 컬러영상이면 True, 그레이스케일이면 False
import cv2
cap = cv2.VideoCapture(0)
w = round(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
out = cv2.VideoWriter('output.avi', fourcc, fps, (w, h))
while True:
ret, frame = cap.read()
if not ret: # return할 내용이 없다면, break
break
out.write(frame)
cv2.imshow('frame', frame)
if cv2.waitKey(10) == 27: # 0.01초마다 ESC버튼의 누름이 확인된다면, 창을 닫기
break
cap.release() # 메모리를 아낄 수 있음.
문제
무료 동영상 사이트에서 동영상 2개를 다운받아서 두 동영상을 연결하는 프로그램을 만들어보기 (두 동영상의 해상도는 같아야 함)
# 동영상 2개를 하나로 연결해보기
import cv2
import numpy as np
cap1 = cv2.VideoCapture('./airport.mp4')
cap2 = cv2.VideoCapture('./china.mp4')
frame_cnt1 = round(cap1.get(cv2.CAP_PROP_FRAME_COUNT))
frame_cnt2 = round(cap2.get(cv2.CAP_PROP_FRAME_COUNT))
print(frame_cnt1)
print(frame_cnt1)
fps1 = cap1.get(cv2.CAP_PROP_FPS)
fps2 = cap2.get(cv2.CAP_PROP_FPS)
print(fps1)
print(fps2)
w = round(cap1.get(cv2.CAP_PROP_FRAME_WIDTH))
h = round(cap1.get(cv2.CAP_PROP_FRAME_HEIGHT))
print('w', w)
print('h', h)
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
out = cv2.VideoWriter('mix.avi', fourcc, fps1, (w, h))
# 1번 영상 열기
for i in range(frame_cnt1):
ret1, frame1 = cap1.read()
cv2.imshow('output', frame1)
out.write(frame1)
if cv2.waitKey(10) == 27:
break
# 2번 영상 열기
for i in range(frame_cnt2):
ret2, frame2 = cap2.read()
cv2.imshow('output', frame2)
out.write(frame2)
if cv2.waitKey(10) == 27:
break
cap1.release()
cap2.release()
out.release()
과제
위 문제에서 1번 동영상이 끝나기 2초전부터 2번 동영상이 좌측에서 시작되어 2초가 끝나면 2번 동영상이 모두 보이는 효과의 프로그램 만들기
# 동영상 2개를 하나로 연결해보기
import cv2
import numpy as np
cap1 = cv2.VideoCapture('./airport.mp4')
cap2 = cv2.VideoCapture('./china.mp4')
frame_cnt1 = round(cap1.get(cv2.CAP_PROP_FRAME_COUNT))
frame_cnt2 = round(cap2.get(cv2.CAP_PROP_FRAME_COUNT))
fps1 = cap1.get(cv2.CAP_PROP_FPS)
fps2 = cap2.get(cv2.CAP_PROP_FPS)
effect_frames = int(fps1) * 2
w = round(cap1.get(cv2.CAP_PROP_FRAME_WIDTH))
h = round(cap1.get(cv2.CAP_PROP_FRAME_HEIGHT))
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
out = cv2.VideoWriter('mix.avi', fourcc, fps1, (w, h))
# 1번 영상 열기
for i in range(frame_cnt1-effect_frames):
ret1, frame1 = cap1.read()
if not ret1:
break
cv2.imshow('frame', frame1)
out.write(frame1)
if cv2.waitKey(10) == 27:
break
# 1번 영상과 2번 영상 합성
for i, x in zip(range(effect_frames), range(1, w, int(w/effect_frames))):
ret1, frame1 = cap1.read()
ret2, frame2 = cap2.read()
delay_frame = frame1
delay_frame[:, :x] = frame2[:, :x]
cv2.imshow('frame', delay_frame)
out.write(delay_frame)
if cv2.waitKey(10) == 27:
break
# 2번 영상 열기
for i in range(effect_frames, frame_cnt2):
ret2, frame2 = cap2.read()
if not ret2:
break
out.write(frame2)
cv2.imshow('frame', frame2)
if cv2.waitKey(10) == 27:
break
cap1.release()
cap2.release()
out.release()