1. 키보드 이벤트
- cv2.waitKey(delay)
- delay: 밀리초 단위 대기(0보다 작거나 같으면 무한정 기다림. 기본값은 0)
- 반환값: 눌려진 키의 아스키 코드값(ESC: 27, ENTER: 13, TAB: 9, SPACE:' )
import cv2
img = cv2.imread('dog.bmp')
cv2.imshow('img',img)
# 화면 띄우고 키가 들어오기 전까지 띄워짐
# 매개변수가 주어지지 않을 때 무한정 기다림
# cv2.waitKey()
while True:
keyvalue = cv2.waitKey()
# ord(): 아스키코드값을 리턴
if keyvalue == ord('i') or keyvalue == ord('I'):
img = ~img # 색상값을 반전
cv2.imshow('img', img)
elif keyvalue == 27:
break
2. 마우스 이벤트
- cv2.setMouseCallback(윈도우이름, 콜백함수, 파라미터)
- 윈도우이름: 마우스 이벤트를 처리할 윈도우 창의 이름=
- 콜백함수: 마우스 이벤트가 발생할 때 호출될 콜백 함수를 설정
- 파라미터: 콜백함수에 전달할 추가적인 정보
- 콜백 함수를 만드는 방법
def 함수명(event, x, y, flags, param):
pass
event: 이벤트 객체
x, y: 마우스 x, y 좌표
flags: 마우스 버튼이 눌리고 있는지, 떼졌는지 여부
param: 추가적인 정보가 전달되었다면 저장
import cv2
import numpy as np
# 전역변수 초기화
oldx = oldy = 0
def on_mouse(event, x, y, flags, param):
global oldx, oldy # 전역변수 사용 선언
if event == cv2.EVENT_LBUTTONDOWN: # 왼쪽 마우스 버튼이 눌렸을 때
oldx, oldy = x, y # 현재 좌표를 oldx, oldy에 저장
print('왼쪽 버튼이 눌렸어요: %d, %d' % (x, y))
elif event == cv2.EVENT_LBUTTONUP: # 왼쪽 마우스 버튼이 떼어졌을 때
print('왼쪽 버튼이 떼졌어요: %d, %d' % (x, y))
elif event == cv2.EVENT_MOUSEMOVE: # 마우스가 움직일 때
if flags & cv2.EVENT_FLAG_LBUTTON: # 왼쪽 버튼을 누른 상태로 움직일 때
cv2.line(param, (oldx, oldy), (x, y), (255, 51, 255), 3) # 선 그리기
cv2.imshow('img', param) # 이미지를 다시 보여주기
oldx, oldy = x, y # 좌표 갱신
# 흰색 배경의 컬러 이미지 생성
img = np.ones((500, 500, 3), dtype=np.uint8) * 255
# 창 이름 설정 및 마우스 콜백 함수 등록
cv2.namedWindow('img')
cv2.setMouseCallback('img', on_mouse, img)
# 이미지 보여주기
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
3. 영상의 화소처리
- 영상의 특정 좌표 픽셀값으 변경하여 출력 영상의 좌표 픽셀을 설정하는 모든 연산
3-1. 밝기 조절
- 영상을 전체적으로 밝게하거나 어둡게 하는 연산
- cv2.add, cv2.subtract, cv2.multiply, cv2.divide
- cv2.addWeighted(): 두 영상의 같은 위치에 존재하는 픽셀값에 대하여 가중합을 계산해서 결과 영상의 픽셀값으로 설정(가중치의 합은 1)
- cv2.absdiff(): 두 영상의 픽셀 값을 빼면 음수가 나올 수 있는데, 음수에 절대값을 취한 값
import cv2
img1 = cv2.imread('./dog.bmp', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('./dog.bmp')
dst1 = cv2.add(img1, 100)
dst2 = cv2.add(img2, (100, 100, 100, 0)) #(255, 255, 255, 3)
dst3 = cv2.subtract(img1, 100)
dst4 = cv2.multiply(img1, 10)
dst5 = cv2.divide(img1, 10)
cv2.imshow('img1', img1)
cv2.imshow('img1', img2)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.imshow('dst3', dst3)
cv2.imshow('dst4', dst4)
cv2.imshow('dst5', dst5)
cv2.waitKey()
3-2. blending (같은 해상도 두 이미지를 add)
import cv2
import matplotlib.pyplot as plt
# 같은 크기의 두 이미지를 add 연산해보기
img1 = cv2.imread('./man.jpg')
img2 = cv2.imread('./turkey.jpg')
# img1 + img2: 255을 넘어갈 경우 해당값의 256을 빼서 표현
dst1 = img1 + img2
# cv2.add(): 255를 넘어갈 경우 255로 고정
dst2 = cv2.add(img1, img2)
img = {'src1': img1, 'src2': 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()
3-3. addWeighted
import cv2
import matplotlib.pyplot as plt
import numpy as np
img1 = cv2.imread('./man.jpg')
img2 = cv2.imread('./turkey.jpg')
alpha = 0.7
dst1 = cv2.addWeighted(img1, alpha, img2, (1 - alpha), gamma=0)
dst2 = img1 * alpha + img2 * (1-alpha)
dst2 = dst2.astype(np. uint8)
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()
3-6. arithmetic
# add, addWeighted, subtract, absdiff
# absdiff(img1, img2)
# matplotlib의 subplot을 이용하여 4가지 연산을 비교
import cv2
import matplotlib.pyplot as plt
import numpy as np
img1 = cv2.imread('./dog.jpg')
img2 = cv2.imread('./square.bmp')
dst1= cv2.add(img1, img2)
dst2 = cv2.addWeighted (img1, 0.5, img2, 0.5, 0)
dst3 = cv2.subtract (img1, img2)
dst4 = cv2.absdiff(img1, img2)
img = {'add': dst1, 'addWeighted': dst2, 'subtract': dst3, 'absdiff': dst4}
for i, (k, v) in enumerate(img.items()):
plt.subplot(2, 2, i + 1)
plt.imshow(v[:, :, ::-1])
plt.title(k)
plt.axis('off')
plt.show()
4. 컬러 영상과 색
- 컬러 영상은 3차원 배열
- numpy.ndarray
- img.shape: (h, w, 3) -> OpenCV에서는 BGR, (높이, 너비, 채널)
- 색상 채널 분리
- cv2.split(영상)
- 색상 채널 결합
- cv2.merge(입력 영상 채널의 리스트 또는 튜플)
import cv2
img = cv2.imread("./candies.png")
print("shape", img.shape)
print("dtype", img.dtype)
b = img[:, :, 0]
g = img[:, :, 1]
r = img[:, :, 2]
cv2.imshow("img", img)
cv2.imshow("b", b)
cv2.imshow("g", g)
cv2.imshow("r", r)
cv2.waitKey()
5.히스토그램(histogram)
- 영상의 픽셀값 분포를 그래프 형태로 표현
- 예) 그레이스케일 영상에서 밝기 정보에 해당하는 픽셀의 개수를 구하고 막대 그래프로 표현
- cv2.calcHist(영상, 히스토그램을 구할 채널, None, 빈의 개수를 나타내는 리스트, 히스토그램 각 차원의 최소값과 최대값으로 구성된 리스트)
# dog.bmp 이미지를 사용하여 3채널로 계산해 히스토그램 그리기
# 단, 하나의 plot에서 RGB 그래프 그리기
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('dog.bmp')
colors = ['b', 'g', 'r']
bgr = cv2.split(img)
for (b, c) in zip(bgr, colors):
hist = cv2.calcHist([b], [0], None, [256], [0, 255])
plt.plot(hist, color=c)
cv2.imshow('img', img)
plt.show()
cv2.waitKey()
영상과 히스토그램의 관계
- 밝은 영상이면 히스토그램이 전체적으로 오른쪽으로 치우쳐져 있음
- 어두운 영상이면 히스토그램이 전체적으로 왼쪽으로 치우쳐져 있음
- 명암비가 확실한 영상이면 히스토그램이 양쪽으로 분포해 있음
6. 균등화, 평탄화(Equalization)
- 히스토그램을 활용하여 이미지의 품질을 개선하기 위한 방법
- 화소값을 0 ~ 255 사이에 고르게 분포하도록 개선
- cv2.equalizeHist()
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('./Hawkes.jpg', cv2.IMREAD_GRAYSCALE)
dst = cv2.equalizeHist(img)
hist1 = cv2.calcHist([img], [0], None, [256], [0, 255])
hist2 = cv2.calcHist([dst], [0], None, [256], [0, 255])
hists = {'hist1':hist1, 'hist2':hist2}
cv2.imshow('img', img)
cv2.imshow('equ', dst)
cv2.waitKey()
plt.figure(figsize=(12,8))
for i, (k, v) in enumerate(hists.items()):
plt.subplot(1,2,i+1)
plt.title(k)
plt.plot(v)
plt.show()
import cv2
img = cv2.imread('./field.bmp')
ycrcb = []
'''
dst = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
ycrcb = cv2.split(dst)
ycrcb = list(ycrcb)
# print(ycrcb)
ycrcb[0] = cv2.equalizeHist(ycrcb[0])
dst = cv2.merge(ycrcb)
dst = cv2.cvtColor(dst, cv2.COLOR_YCrCb2BGR)
'''
# 문제
# split(), merge()를 사용하지 않고 슬라이싱과 인덱싱을 이용하여 위 예제와 동일하게 결과이미지 만들기
dst = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
dst[:,:,0] = cv2.equalizeHist(dst[:, :, 0])
dst = cv2.cvtColor(dst, cv2.COLOR_YCrCb2BGR)
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()
7. CLAHE(Contrast Limited Adaptive Histogram Equalization)
- 평탄화를 하면 이미지의 밝은 부분이 날라가는 현상을 보정하기 위해 사용
- 이미지의 일정한 영역으로 나누어 평탄화를 적용
- 객체 = cv2.createCLAHE(대비, 영역 크기)
- 객체.apply(영상)
import cv2
img = cv2.imread('./field.bmp')
dst = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
img_eq = dst.copy()
img_clahe = dst.copy()
img_eq[:, :, 0] = cv2.equalizeHist(img_eq[:, :, 0])
img_eq = cv2.cvtColor(img_eq, cv2.COLOR_YCrCb2BGR)
clahe = cv2.createCLAHE(clipLimit=4, tileGridSize=(4, 4))
img_clahe[:, :, 0] = clahe.apply(img_clahe[:, :, 0])
img_clahe = cv2.cvtColor(img_clahe, cv2.COLOR_YCrCb2BGR)
cv2.imshow('img', img)
cv2.imshow('img_eq', img_eq)
cv2.imshow('img_clahe', img_clahe)
cv2.waitKey()
8. 정규화(Normalization)
- 이미지에서도 정규화가 필요한 경우가 있음
- 특정 영역에 값이 몰려 있는 경우 화질을 개선하고, 이미지 간의 연산 시 서로 조건이 다른 경우 같은 조건으로 변경
- cv2.normalize(정규화 이전 영상, 정규화 이후 영상, 정규화 구간1, 정규화 구간2, 정규화 알고리즘 선택 플래그)
- 정규화 구간2: 구간 정규화가 아닌 경우는 사용하지 않음
- 정규화 알고리즘 선택 플래그
- cv2.NORM_MINMAX: 정규화 구간1 ~ 정규화 구간2를 구간으로 하는 정규화
- cv2.NORM_L1: 전체 합으로 나눔
- cv2.NORM_L2: 단위 벡터로 정규화
- cv2.NORM_INF: 최대값으로 나눔
img_norm1:
- 수동으로 계산하여 정규화합니다.
- 먼저 이미지를 float32 타입으로 변환한 후, 각 픽셀 값에서 최소값을 뺍니다.
- 그 결과를 최대값과 최소값의 차이로 나눈 후, 255를 곱하여 0에서 255 사이로 스케일링합니다.
- 마지막으로, 다시 uint8 타입으로 변환합니다.
img_norm2:
- OpenCV의 cv2.normalize 함수를 사용하여 정규화합니다.
- 이 함수는 입력 이미지의 최소 및 최대 값을 0과 255로 매핑하여 자동으로 정규화를 수행합니다.
9. 색상 추출
9-1. inRange()
- 이미지에서 지정된 범위 안에 픽셀을 찾아냄
- cv2.inRange
RGB 녹색계열
0 <= B <= 100
128 <= G <= 255
0 <= R <= 100
HSV 녹색계열
50 <= H <= 80
150 <= S <= 255
0 <= V <= 255
import cv2
img = cv2.imread('candies.png')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
dst = cv2.inRange(hsv, (50, 150, 0), (80, 255, 255))
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()
9-2. copyTo()
- 마스크 연산을 지원하는 픽셀 값 복사 함수
- cv2.copyTo(영상, 마스크, 출력영상=None)
문제
# 영상을 다운받아 woma.mp4를 합성해보자
import cv2
cap1 = cv2.VideoCapture('./woman.mp4')
cap2 = cv2.VideoCapture('./back.mp4')
print('가로 사이즈:', int(cap1.get(cv2.CAP_PROP_FRAME_WIDTH)))
print('세로 사이즈:', int(cap1.get(cv2.CAP_PROP_FRAME_HEIGHT)))
print('1번 프레임 수:', int(cap1.get(cv2.CAP_PROP_FRAME_COUNT)))
print('2번 프레임 수:', int(cap2.get(cv2.CAP_PROP_FRAME_COUNT)))
print('1번 fps:', int(cap1.get(cv2.CAP_PROP_FPS)))
isKeypress = False
while True:
ret1, frame1 = cap1.read()
if not ret1:
break
if isKeypress:
ret2, frame2 = cap2.read()
if not ret2:
break
hsv = cv2.cvtColor(frame1, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, (50, 150, 0), (80, 255, 255))
cv2.copyTo(frame2, mask, frame1)
cv2.imshow('frame1', frame1)
key = cv2.waitKey(10)
if key == ord(' '):
isKeypress = not isKeypress
elif key == 27:
break
cap1.release()
cap2.release()
스페이스바 누르면 아래와 같이 영상 합성
'컴퓨터비전(CV)' 카테고리의 다른 글
6. Open CV5 (0) | 2024.07.14 |
---|---|
5. OpenCV4 (1) | 2024.07.08 |
4. Open CV3 (0) | 2024.07.08 |
2. Open CV1 (0) | 2024.07.08 |
1. 컴퓨터비전(CV) (0) | 2024.07.08 |