vscode에서 실행
가상환경 만들기(윈도우)
cmd 창에서 다음을 입력한다.
Python -m venv venv
cd venv/Scripts/
activate
설치
cmd 창에서 다음을 입력한다.
pip install opencv-python
import cv2
print('현재 openCV 버전: ', cv2.__version__)
import cv2
# 그레이스케일 영상
img1 = cv2.imread('./images/dog.bmp', cv2.IMREAD_GRAYSCALE)
print(img1)
# 트루컬러 영상
# img2 = cv2.imread('./images/dog.bmp')
img2 = cv2.imread('./images/dog.bmp', cv2.IMREAD_COLOR) # cv2.IMREAD_COLOR 생략 가능
# 기본적으로 컬러 이미지로 읽기(B, G, R 순서)
print(img2)
cv2.imshow('img1', img1) # BGR
cv2.imshow('img2', img2)
cv2.waitKey()
waitKey() : 이미지가 떴을 때 키보드의 아무 키나 누르면 이미지가 사라짐.
import cv2
import matplotlib.pyplot as plt
# subplot이용하여 left plot에는 그레이스케일 영상, right plot에는 컬러영상을 출력
img_gray = cv2.imread('./images/dog.bmp', cv2.IMREAD_GRAYSCALE)
img_color = cv2.imread('./images/dog.bmp') # RGB 순서 아닌 BGR로 가져옴. 그래서 색이 이상하게 나옴. matplotlib에서는 순서가 RGB여야 한다.
img_color = cv2.cvtColor(img_color, cv2.COLOR_BGR2RGB) # BGR -> RGB 바꿔줌.
plt.subplot(121) # 1행 2열 첫번째
plt.axis('off') # 격자 없애줌.
plt.imshow(img_gray, cmap='gray')
plt.subplot(122) # 1행 2열 두번째
plt.axis('off')
plt.imshow(img_color)
plt.show()
import cv2
import numpy as np
img_gray = cv2.imread('./images/dog.bmp', cv2.IMREAD_GRAYSCALE)
print('img_gray type: ', type(img_gray))
print('img_gray shape: ', img_gray.shape) # (세로, 가로)
print('img_gray dtype: ', img_gray.dtype)
img_color = cv2.imread('./images/dog.bmp')
print('img_color type: ', type(img_color))
print('img_color shape: ', img_color.shape) # (세로, 가로, 색상)
print('img_color dtype: ', img_color.dtype)
# (364, 548, 3)
h, w = img_color.shape[:2]
print(f'이미지 사이즈: {w} * {h}')
# 그레이스케일 영상인지, 컬러영상인지 구분하기
if len(img_color.shape) == 3:
print('컬러 영상')
elif len(img_color.shape) == 2:
print('그레이스케일 영상')
img1 = np.zeros((240, 320, 3), dtype=np.uint8) # 240x320 크기의 검은색 이미지 생성
img2 = np.empty((240, 320), dtype=np.uint8) # 240x320 크기의 랜덤한 값의 색상 이미지 생성
img3 = np.ones((240, 320), dtype=np.uint8) * 120 # 120 밝기 정도가 바뀜.
img4 = np.full((240, 320, 3), (255, 102, 255),dtype=np.uint8)
# for x in range(h):
# for y in range(w):
# img_color[x, y] = (255, 102, 255)
img1[:, :] = (255, 102, 255) # :는 모든 행, 모든 열을 뜻하므로 → img1[:, :]는 이미지 전체 영역(= 모든 픽셀)을 가리킨다.
cv2.imshow('img_color', img_color)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.imshow('img4', img4)
while True:
keyvalue = cv2.waitKey()
if keyvalue == ord('i') or keyvalue == ord('I'):
img_color = ~img_color # i or I 키 누르면 이미지 반전
cv2.imshow('img_color', img_color)
elif keyvalue == 27: # ESC 키를 누르면 종료
break
cv2.waitKey()
img_gray type: <class 'numpy.ndarray'>
img_gray shape: (364, 548)
img_gray dtype: uint8
img_color type: <class 'numpy.ndarray'>
img_color shape: (364, 548, 3)
img_color dtype: uint8
이미지 사이즈: 548*364
컬러 영상
uint8
u → unsigned (부호 없음: 음수 없음)
int → 정수
8 → 8비트 (1바이트)
즉, 0부터 255 사이의 정수만 저장할 수 있는 자료형
import cv2
import numpy as np
oldx = oldy = 0
def on_mouse(event, x, y, flags, param):
global oldx, oldy
# print(event)
if event == cv2.EVENT_LBUTTONDOWN: # 왼쪽 버튼 클릭
print('왼쪽 버튼이 눌렸어요: %d, %d' % (x, y))
oldx, oldy = x, y
elif event == cv2.EVENT_LBUTTONUP:
print('왼쪽 버튼이 떼졌어요: %d, %d' % (x, y))
elif event == cv2.EVENT_MOUSEMOVE:
print('마우스가 이동하고 있어요: %d, %d' % (x, y))
if flags: # 왼쪽 버튼이 눌린 상태에서 이동
print('드래그 중이예요')
cv2.line(img, (oldx, oldy), (x, y), (255, 51, 255), 3) # 선 그리기
cv2.imshow('img', img)
oldx, oldy = x, y
img = np.ones((500, 500, 3), dtype=np.uint8) * 255 # 흰색 배경 이미지 생성
cv2.namedWindow('img')
cv2.rectangle(img, (50, 200, 150, 100), (0, 255, 0), 3)
# 왼쪽 위: (50, 200)
# 오른쪽 아래: (200, 300) → 가로 150, 세로 100
# 초록색 선 ((0, 255, 0)) 굵기 3으로 테두리만 그림
cv2.rectangle(img, (300, 200, 150, 100), (0, 255, 0), -1) # -1 : 색상 채워짐
cv2.circle(img, (150, 400), 50, (255, 0, 0), 3) # 원 그리기
# 중심 좌표: (150, 400)
# 반지름: 50
# 색상: 파란색 ((255, 0, 0))
# 굵기: 3
cv2.putText(img, 'Hello', (50, 300), cv2.FONT_HERSHEY_DUPLEX, 0.7, (0, 0, 0))
# 'Hello': 출력할 문자열
# (50, 300): 시작 좌표 (왼쪽 아래 기준)
# cv2.FONT_HERSHEY_DUPLEX: 폰트 스타일
# 0.7: 폰트 크기 (scale)
# (0, 0, 0): 글자 색 (검정)
cv2.imshow('img', img)
cv2.setMouseCallback('img', on_mouse)
cv2.waitKey()
import cv2
import sys
cap = cv2.VideoCapture('./movies/232538_tiny.mp4')
if not cap.isOpened():
print('동영상을 불러올 수 없음')
sys.exit()
print('동영상을 불러올 수 있음')
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
print(width)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
print(height)
frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
print(frame_count) # 총 프레임 수
fps = cap.get(cv2.CAP_PROP_FPS)
print(fps) # 초당 프레임 수
while True:
ret, frame = cap.read() # 프레임 읽기(ret: 성공 여부, frame: 프레임 이미지)
if not ret: # 프레임을 더 이상 읽을 수 없으면 종료
break
cv2.imshow('frame', frame) # 프레임 표시
if cv2.waitKey(10) == 27: # 10/1000 초. 0.01초동안 계속 돔. # ESC 키를 누르면 종료
break
cap.release() # 비디오 캡처 객체 해제
영상이 출력된다.
픽사베이 사이트에서 영상을 다운 받을 수 있다.
import cv2
import sys
cap = cv2.VideoCapture(0) # 0은 기본 카메라를 의미
if not cap.isOpened():
print('카메라를 열 수 없음')
sys.exit()
print('카메라 연결 성공')
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
print(width)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
print(height)
fps = cap.get(cv2.CAP_PROP_FPS)
print(fps) # 초당 프레임 수
while True:
ret, frame = cap.read() # 프레임 읽기(ret: 성공 여부, frame: 프레임 이미지)
if not ret: # 프레임을 더 이상 읽을 수 없으면 종료
break
cv2.imshow('frame', frame) # 프레임 표시
if cv2.waitKey(10) == 27: # 10/1000 초. 0.01초동안 계속 돔. # ESC 키를 누르면 종료
break
cap.release() # 비디오 캡처 객체 해제
웹캠이 설치되어 있는 컴퓨터라면, 웹캠을 통해 영상이 출력되어 나온다.
웹캠이 설치되어 있지 않다면, 다음과 같이 카메라를 열 수 없다는 메시지가 출력된다.
import cv2
cap1 = cv2.VideoCapture('./movies/232538_tiny.mp4')
cap2 = cv2.VideoCapture('./movies/276624_tiny.mp4')
# 두 비디오를 합치려면 두 비디오의 해상도가 같아야 한다.
w = int(cap1.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(cap1.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_count1 = int(cap1.get(cv2.CAP_PROP_FRAME_COUNT))
frame_count2 = int(cap2.get(cv2.CAP_PROP_FRAME_COUNT))
fps1 = cap1.get(cv2.CAP_PROP_FPS)
fps2 = cap2.get(cv2.CAP_PROP_FPS)
print(w, h)
print(frame_count1, frame_count2)
print(fps1, fps2)
fourcc = cv2.VideoWriter.fourcc(*'DIVX')
out = cv2.VideoWriter('mix.avi', fourcc, fps1, (w, h))
for i in range(frame_count1):
ret, frame = cap1.read()
cv2.imshow('output', frame)
out.write(frame)
if cv2.waitKey(10) == 27: # ESC 키를 누르면 종료
break
for i in range(frame_count2):
ret, frame = cap2.read()
cv2.imshow('output', frame)
out.write(frame)
if cv2.waitKey(10) == 27: # ESC 키를 누르면 종료
break
cap1.release()
cap2.release()
out.release()
두 개의 영상이 연이어 나온다.
이후 mix.avi 라는 파일이 생성되어, 두 개의 영상이 합쳐진 결과물이 하나의 파일로 생성된다.
비디오를 저장할 때 어떤 압축 방식(코덱)으로 저장할지 지정해줘야 하는데,
그것을 이 fourcc를 통해 설정한다.
자주 쓰는 FourCC 코드
| 코드 | 설명 | 파일 확장자 |
|---|---|---|
'DIVX' | DivX MPEG-4 | .avi |
'XVID' | XviD MPEG-4 | .avi |
'MJPG' | Motion JPEG | .avi |
'MP4V' | MPEG-4 Video | .mp4 |
'X264' | H.264 (설치 필요) | .mp4 |
| 코드 | 설명 |
|---|---|
cap.release() | 비디오 입력 (예: 카메라, 파일) 닫기 |
out.release() | 비디오 출력 (파일 저장 객체) 닫기 |
안 해주면 웹캠이 다른 앱에서 안 열리거나, 시스템 리소스를 계속 차지할 수 있고,
저장 중인 영상 파일이 깨지거나, 재생이 안 될 수도 있다.
import cv2
cap = cv2.VideoCapture(0) # 0은 기본 카메라를 의미
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
fourcc = cv2.VideoWriter.fourcc(*'DIVX')
out = cv2.VideoWriter('camera.avi', fourcc, fps, (w, h))
while True:
ret, frame = cap.read() # 프레임 읽기(ret: 성공 여부, frame: 프레임 이미지)
if not ret: # 프레임을 더 이상 읽을 수 없으면 종료
break
cv2.imshow('frame', frame) # 프레임 표시
out.write(frame) # 프레임을 비디오 파일에 저장
if cv2.waitKey(10) == 27: # 10/1000 초. ESC 키를 누르면 종료
break
cap.release()
out.release()
웹캠을 통해 영상을 찍을 수 있다.
이후 camera.avi 라는 파일이 생성되어 웹캠으로 찍은 영상이 저장된다.
import cv2
img1 = cv2.imread('./images/dog.bmp', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('./images/dog.bmp') # 기본적으로 컬러 이미지로 읽기(B, G, R 순서)
dst1 = cv2.add(img1, 100) # 그레이스케일 이미지에 100 더하기
dst2 = cv2.add(img2, (100, 100, 100)) # 컬러 이미지에 (100, 100, 100) 더하기
dst3 = cv2.subtract(img1, 100) # 그레이스케일 이미지에서 100 빼기
dst4 = cv2.multiply(img1, 10)
dst5 = cv2.divide(img1, 10) # 그레이스케일 이미지에서 10으로 나누기
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.imshow('dst3', dst3)
cv2.imshow('dst4', dst4)
cv2.imshow('dst5', dst5)
cv2.waitKey()
import cv2
import matplotlib.pyplot as plt
img1 = cv2.imread('./images/man.jpg')
img2 = cv2.imread('./images/turkey.jpg')
# 0~255 값을 벗어나면 256으로 나눈 나머지가 됨
dst1 = img1 + img2
# 0~255 값을 벗어나도 0 또는 255 값으로 보정
dst2 = cv2.add(img1, img2)
# cv2.imshow('dst1', dst1)
# cv2.imshow('dst2', dst2)
# cv2.waitKey()
img = {'img1': img1, 'img2': img2, 'dst1': dst1, 'dst2': dst2}
for i, (k, v) in enumerate(img.items()):
plt.subplot(2, 2, i + 1)
plt.imshow(v[:, :, ::-1])
plt.title(k)
plt.show()
v[:, :, ::-1]
v는 3차원 이미지 배열 (Height x Width x Channels)
v[:, :, ::-1]는 채널 축(axis=2)을 역순으로 뒤집는 표현이다.
::는 슬라이싱,::-1은 거꾸로 뒤집기
→ BGR → RGB로 바뀐다.
import cv2
import matplotlib.pyplot as plt
img1 = cv2.imread('./images/dog.jpg')
img2 = cv2.imread('./images/square.bmp')
dst1 = cv2.add(img1, img2)
# 가중치 합성
# 두 이미지를 비율로 섞음
dst2 = cv2.addWeighted(img1, 0.5, img2, 0.5, 0) # 0.5, 0.5 비율로 더하고, 추가 더할 값은 0
dst3 = cv2.subtract(img1, img2)
# 절대 차이
# 두 이미지 간의 절대 차이값: abs(img1(x, y) - img2(x, y))
# 예) img1 = 80, img2 = 100 = abs(-20) = 20
dst4 = cv2.absdiff(img1, img2)
img = {'dst1': dst1, 'dst2': dst2, 'dst3': dst3, 'dst4': dst4}
for i, (k, v) in enumerate(img.items()):
plt.subplot(2, 2, i + 1)
plt.imshow(v[:, :, ::-1])
plt.title(k)
plt.show()
import cv2
import matplotlib.pyplot as plt
img1 = cv2.imread('./images/candies.png', cv2.IMREAD_GRAYSCALE)
# 히스토그램
# 이미지 히스토그램: 밝기(또는 색상) 값의 분포를 그래프로 표현
# 어떤 픽셀이 밝기 0(검정)인지 255(흰색)인지, 각 값이 몇 개나 있는지를 확인
# images : 대상 이미지 리스트
# channel : 분석할 채널 번호(B:0, G:1, R:2)
# mask : 분석할 영역 마스크(None: 전체)
# histSize : 히스토그램의 빈 개수
# ranges : 값의 범위(0~255)
hist1 = cv2.calcHist([img1], [0], None, [256], [0, 255])
plt.figure(figsize = (10, 4))
plt.subplot(1, 2, 1)
plt.plot(hist1, color='gray')
img2 = cv2.imread('./images/candies.png')
print('shape: ', img2.shape)
print('dtype: ', img2.dtype)
'''
b = img[:, :, 0]
g = img[:, :, 1]
r = img[:, :, 2]
'''
colors = ['b', 'g', 'r']
channels = cv2.split(img2)
plt.subplot(1, 2, 2)
for ch, color in zip(channels, colors):
hist = cv2.calcHist([ch], [0], None, [256], [0, 255])
plt.plot(hist, color=color, label=color.upper())
b, g, r = cv2.split(img2)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('b', b)
cv2.imshow('g', g)
cv2.imshow('r', r)
plt.plot(hist1)
plt.show()
cv2.waitKey()
이미지들을 보면,
b 이미지 : 파란색 계열이 밝게 나타난 것을 확인할 수 있고,
g 이미지 : 노랑~초록색 계열이 밝게 나타난 것을 확인할 수 있다.
r 이미지 : 빨간색 계열이 밝게 나타난 것을 확인할 수 있다.
import cv2
import matplotlib.pyplot as plt
img1 = cv2.imread('./images/Hawkes.jpg', cv2.IMREAD_GRAYSCALE)
# 히스토그램 평활화
# 이미지의 전체 밝기 분포를 고르게 퍼뜨려 명암 대비를 향상시키는 기법
dst1 = cv2.equalizeHist(img1)
img2 = cv2.imread('./images/field.bmp')
# YCrCb 색 공간
# Y : 밝기(명도), Cr : 빨강 계열 색상 정보, Cb : 파랑 계열 색상 정보
dst2 = cv2.cvtColor(img2, cv2.COLOR_BGR2YCrCb)
'''
ycrcb = cv2.split(dst2)
ycrcb = list(ycrcb)
# print(ycrcb)
ycrcb[0] = cv2.equalizeHist(ycrcb[0]) # Y 채널에 대해서만 히스토그램 평활화
dst2 = cv2.merge(ycrcb)
dst2 = cv2.cvtColor(dst2, cv2.COLOR_YCrCb2BGR)
'''
dst2[:, :, 0] = cv2.equalizeHist(dst2[:, :, 0])
dst2 = cv2.cvtColor(dst2, cv2.COLOR_YCrCb2BGR)
# img1: 원본 이미지 또는 배열
# None: 출력 배열(None이면 새로 생성)
# 0: 정규화 후 최소값
# 255: 정규화 후 최대값
# cv2.NORM_MINMAX: 정규화 방식
dst3 = cv2.normalize(img1, None, 0, 255, cv2.NORM_MINMAX)
hist1 = cv2.calcHist([img1], [0], None, [256], [0, 255])
hist2 = cv2.calcHist([dst1], [0], None, [256], [0, 255])
hist3 = cv2.calcHist([dst3], [0], None, [256], [0, 255])
hists = {'hist1': hist1, 'hist2': hist2, 'hist3': hist3}
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.imshow('dst3', dst3)
plt.figure(figsize=(12, 8))
for i, (k, v) in enumerate(hists.items()):
plt.subplot(1, 3, i + 1)
plt.title(k)
plt.plot(v)
plt.show()
cv2.waitKey()
import cv2
# HSV
# 색을 표현하는 한 방식으로 이미지 처리와 색상 추출에서 매우 자주 사용되는 장점을 갖음
# H: 색상 (빨강, 초록, 파랑 등, 0~179), S: 채도(색의 선명함, 0~255), V: 명도(밝기, 0~255)
img = cv2.imread('./images/candies.png')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
airplane = cv2.imread('./images/airplane.bmp')
mask = cv2.imread('./images/mask_plane.bmp')
field = cv2.imread('./images/field.bmp')
'''
색상 H 값 (OpenCV 기준)
빨강 (Red) 0 또는 179
주황 (Orange) 10~20
노랑 (Yellow) 20~30
초록 (Green) 40~85
파랑 (Blue) 90~130
> 보라 (Violet) 130~160
채도: 150 ~ 255
명도: 0 ~ 255
'''
dst = cv2.inRange(hsv, (90, 150, 0), (130, 255, 255))
# copyTo
# 마스크를 이용한 선택적 복사
temp = cv2.copyTo(airplane, mask)
cv2.copyTo(airplane, mask, field) # 마스크를 이용하여 field에 airplane을 복사
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.imshow('temp', temp)
cv2.imshow('field', field)
cv2.waitKey()
cv2.copyTo(src, mask, dst)
| 매개변수 | 설명 |
|---|---|
src | 복사할 원본 이미지 (source) |
mask | 복사할 위치를 지정하는 마스크 (0 또는 255로 구성된 단일 채널 이미지) |
dst | 복사 대상 이미지 (destination) — 이곳에 src의 픽셀이 복사됨 |
import cv2
cap1 = cv2.VideoCapture('./movies/woman.mp4')
cap2 = cv2.VideoCapture('./movies/sea.mp4')
w = int(cap1.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(cap1.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_count1 = int(cap1.get(cv2.CAP_PROP_FRAME_COUNT))
frame_count2 = int(cap2.get(cv2.CAP_PROP_FRAME_COUNT))
fps = int(cap1.get(cv2.CAP_PROP_FPS))
while True:
ret1, frame1 = cap1.read()
if not ret1:
break
ret2, frame2 = cap2.read()
if not ret2:
break
hsv = cv2.cvtColor(frame1, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, (50, 150, 0), (70, 255, 255))
cv2.copyTo(frame2, mask, frame1)
cv2.imshow('frame1', frame1)
if cv2.waitKey(10) == ord(' '):
cv2.waitKey()
elif cv2.waitKey(10) == 27: # ESC 키를 누르면 종료
break
cap1.release()
cap2.release()
첫 번째 cv2.waitKey(10) → 스페이스바인지 확인 → 영상 멈춤
두 번째 cv2.waitKey(10) → ESC 키인지 확인 → 영상 종료