본문 바로가기
컴퓨터비전(CV)

3. Open CV2

by 곽정우 2024. 7. 8.

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

원본
색상값을 반전(키보드 i를 눌렀을 때)

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()

원본
정규화
CLAHE  적용

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