본문 바로가기

8. OpenCV | CV

7/25(화) IT K-DT(97일차) / 4.KeyEvent~11.정규화

 

 

4. Key Event

 

1. 키보드 이벤트
    cv2.waitKey(delay)
    delay: 밀리초 단위 대기(0보다 작거나 같으면 무한정 기다림. 기본값은 0)
    return: 눌려진 아스키 코드값(ESC: 27, ENTER: 13, TAB: 9 ...)

 

' i '를 누를 시 이미지 색상이 반전되는 예제

 

import cv2

img = cv2.imread('./dog.bmp')
cv2.imshow('image', img)
cv2.waitKey()

while True:
    keyvalue = cv2.waitKey()
    if keyvalue == ord('i') or keyvalue == ord('I'): # ord(): 아스키코드값을 리턴시켜주는 함수
        img = ~img        # 소문자나 대문자 i를 사용하면 img값을 반전
        cv2.imshow('image', img)
    elif keyvalue == 27:
        break

 

 

 

5. Mouse Event

마우스 이벤트
    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: # 만약  event가 마우스 왼쪽버튼 클릭 이라면,
        oldx, oldy = x, y
        print('왼쪽 버튼이 눌렸습니다: %d, %d' % (x, y))
    elif event == cv2.EVENT_LBUTTONUP: # 만약 event가 마우스 왼쪽버튼 뗌 이라면,
        print('왼쪽 버튼이 떼어졌습니다: %d, %d' % (x, y))
    elif event == cv2.EVENT_MOUSEMOVE: # 만약 event가 마우스 움직임 이라면,
        if flags & cv2.EVENT_FLAG_LBUTTON: # 마우스의 드래그를 나타냄
            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 # 500*500의 컬러형태의 흰색배경의 창
cv2.namedWindow('img') # namedWindow(): 창에 이름을 부여하는 함수
cv2.setMouseCallback('img', on_mouse, img) # 파라미터로 이미지 객체를 넘겨줌
cv2.imshow('img', img)
cv2.waitKey()

 

 

 

6. 화소처리

영상의 특정 좌표 픽셀값을 변경하여 출력 영상의 좌표 픽셀을 설정하는 모든 연산
1) 밝기 조절
    영상을 전체적으로 밝게 하거나 어둡게 하는 연산
        cv2.add(첫번째영상, 두번째영상)
        cv2.subtract(첫번째영상, 두번째영상)
        cv2.multiply(첫번째영상, 두번째영상)
        cv2.divide(첫번째영상, 두번째영상)
2) 가중치 합(weighted sum)
    두 영상의 같은 위치에 존재하는 픽셀값에 대하여 가중합을 계산하여 결과 영상의 픽셀 값으로 설정함
        cv2.addWeighted(첫번째영상, 알파값, 두번째영상, 감마값)
        알파값: 첫번째 영상에 지정할 가중치. 가중치의 합은 1이 됨
        감마값: 연산 결과에 가감할 함수. 기본값은 0
3) 두 영상의 차이(absdiff)
    두 영상의 픽셀값을 빼면 음수가 나올 수 있는데, 이 음수에 절대값을 취한 값
        cv2.absdiff(첫번째영상, 두번째영상)

 

import cv2

src1 = cv2.imread('./dog.bmp', cv2.IMREAD_GRAYSCALE) #  그레이스케일
src2 = cv2.imread('./dog.bmp') # 컬러

dst1 = cv2.add(src1, 100) # 모든 픽셀에 100을 더함 // 숫자가 올라갈수록 밝아짐
print(dst1)
cv2.imshow('dst1', dst1)

dst2 = cv2.add(src2, (100, 100, 100, 0)) # 각각의 BGR채널에 100을 더함 // 숫자가 올라갈수록 밝아짐
cv2.imshow('dst2', dst2)
print('-----------------------------------------------')

dst3 = cv2.subtract(src1, 100)  # 모든 픽셀에 100을 뺌 // 숫자가 내려갈수록 어두워짐
print(dst3)
cv2.imshow('dst3', dst3)
print('-----------------------------------------------')

dst4 = cv2.multiply(src1, 10) # 모든 픽셀에 10을 곱함
print(dst4)
cv2.imshow('dst4', dst4)
print('-----------------------------------------------')

dst5 = cv2.divide(src1, 10) # 모든 픽셀에 10을 나눔
print(dst5)
cv2.imshow('dst5', dst5)
print('-----------------------------------------------')
cv2.waitKey()

 

 

# 두 개의 이미지를 합성

import cv2
import matplotlib.pyplot as plt

src1 = cv2.imread('./bird.jpg')
src2 = cv2.imread('./mountain.jpg')
# src1과 sec2의 사이즈를 동일하게 변경
new_height = 395
new_width = 640
src2 = cv2.resize(src2, (new_width, new_height))

dst1 = src1 + src2
dst2 = cv2.add(src1, src2)

img = {'src1':src1, 'src2':src2, '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.axis('off')
plt.show()

 

 

import cv2
import numpy as np

src1 = cv2.imread('./bird.jpg')
src2 = cv2.imread('./mountain.jpg')

# src1과 sec2의 사이즈를 동일하게 변경
new_height = 395
new_width = 640
src2 = cv2.resize(src2, (new_width, new_height))

# 가중치 설정
alpha = 0.7

# 1. 손으로 연산
dst1 = src1*alpha + src2*(1-alpha)
dst1 = dst1.astype(np.uint8) # 소숫점을 자르기

# 2. addWeighted로 연산
dst2 = cv2.addWeighted(src1, alpha, src2, (1-alpha), 0)

cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()

 

 

# square.bmp (256, 256)
# 같은 크기의 이미지와 연산
# add연산, addWeighted연산, subtract연산, absdiff연산
# matplotlib의 subplot을 이용해서 이미지 비교

import cv2
import matplotlib.pyplot as plt
import numpy as np

src1 = cv2.imread('./square.bmp')
src2 = cv2.imread('./bird.jpg')
new_height = 256
new_width = 256
src2 = cv2.resize(src2, (new_width, new_height))

alpha = 0.7

dst1 = cv2.add(src1, src2)
dst1 = dst1.astype(np.uint8)
dst2 = cv2.addWeighted(src1, alpha, src2, (1-alpha), 0)
dst3 = cv2.subtract(src1, src2)
dst4 = cv2.absdiff(src1, src2)

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

 

 

7. 컬러 영상과 색상

    컬러 영상은 3차원 배열.
        numpy.ndarray
        img.shape: (h, w, 3) → OpenCV에서는 BGR
    색상 채널의 분리가 가능
        cv2.split(영상)
    색상 채널의 결합도 가능
        cv2.merge(영상의 리스트형태or튜플형태)

 

import cv2

src = cv2.imread('./candies.png')
# BGR순으로 가져옴 # png는 alpha채널이 있으므로 alpha채널을 포함해서 가져올 수 있음.
# src = cv2.imread('./candies.png', cv2.IMREAD_UNCHANGED)
print('shape:', src.shape)
print('dtype:', src.dtype)

'''
# 1. bgr을 리스트를 이용해서 분리하는 방법
b = src[:,:,0]
g = src[:,:,1]
r = src[:,:,2]
'''

# 2. bgr을 split()함수를 이용해서 분리하는 방법
b, g, r = cv2.split(src)

cv2.imshow('src', src)
cv2.imshow('b', b)
cv2.imshow('g', g)
cv2.imshow('r', r)
cv2.waitKey()

 

 

8. 히스토그램(histogram)

cv2.calcHist(영상, 히스토그램을구할채널, None, 빈의개수를나타내는리스트(0),

                     히스토그램각차원의최소값과최대값으로구성된리스트)
영상의 픽셀값 분포를 그래프 형태로 표현한 것(0:255) //
예) 그레이스케일 영상에서 밝기정보에 해당하는 픽셀의 개수를 구하고 막대그래프로 표현.
영상과 히스토그램의 관계
- 밝은 영상이면 히스토그램이 전체적으로 오른쪽으로 치우쳐져있음
- 어두운 영상이면 히스토그램이 전체적으로 왼쪽으로 치우쳐져있음
- 명암비가 확실한 영상이면 히스토그램이 양쪽으로 분포해있음

 

import cv2
import matplotlib.pyplot as plt

src = cv2.imread('./dog.bmp', cv2.IMREAD_GRAYSCALE)
hist = cv2.calcHist([src], [0], None, [256], [0, 255])
# calcHist(): 여러 개를 비교분석하는 함수. 인자는 모두 list로 넣어줌.
cv2.imshow('src', src)
plt.plot(hist)
plt.axis('off')
plt.show()
cv2.waitKey()

 

 

# dog.bmp 이미지를 사용하여 3채널(RGB)로 계산해 히스토그램 그리기

import cv2
import matplotlib.pyplot as plt

src = cv2.imread('./dog.bmp')

colors = ['b', 'g', 'r']
bgr = cv2.split(src)  # src를 한꺼번에 받으면 list의 형태가 됨

for (b, c) in zip(bgr, colors):
    hist=cv2.calcHist([b], [0], None, [256], [0,255])
    plt.plot(hist, color=c)
cv2.imshow('src', src)
plt.show()
cv2.waitKey()

 

 

9. 균등화(Equalization)

히스토그램을 활용하여 이미지의 품질을 개선하기 위한 방법
화소값을 0부터 255 사이에 고르게 분포하도록 개선
    cv2.equalizeHist(영상)
YCbCr
    - 색 공간을 밝기 정보로 표현하는 방식을 택함
    - Y: 밝기 정보
    - YCbCr을 줄여서 YCC라고 부르기도 함
HSV
    - 색을 표현하는 방법이자 배치하는 방식
    - 색상(빨강, 녹색, 파랑 등을 H값), 채도(선명도, S값), 명도(빛의 밝기, V값)의 좌표를 써서 특정 색을 지정

 

import cv2
import matplotlib.pyplot as plt

src = cv2.imread('./Hawkes.jpg', cv2.IMREAD_GRAYSCALE)
equ = cv2.equalizeHist(src)
hist1 = cv2.calcHist([src], [0], None, [256], [0, 255])
hist2 = cv2.calcHist([equ], [0], None, [256], [0, 255])

hists = {'hist1':hist1, 'hist2':hist2}
cv2.imshow('src', src)
cv2.imshow('equ', equ)
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()

 

 

# field 사진의 명암비가 변화된 것을 확인

import cv2


src = cv2.imread('./field.bmp')
ycrcb = []

# 첫번째 방법
'''
dst = cv2.cvtColor(src, cv2.COLOR_BGR2YCrCb) # BGR을 YCrCb로 바꿔서 색의 표현방법을 바꿈.
ycrcb = cv2.split(dst) # 색상 분할을 일으킴
ycrcb = list(ycrcb)
# print(ycrcb) # ndarray를 list로 묶음. equalizeHist에 list의 형식으로 넣어야 하기 때문에 다시 감싸줌
ycrcb[0] = cv2.equalizeHist(ycrcb[0])
dst = cv2.merge(ycrcb) # 색을 분할한 후 다시 합병을 시켜야 함
dst = cv2.cvtColor(dst, cv2.COLOR_YCrCb2BGR) # 다시 Y CrCb를 BGR로 바꾸어줌
'''

# 두번째 방법
dst = cv2.cvtColor(src, cv2.COLOR_BGR2YCrCb)
dst[:,:,0] = cv2.equalizeHist(dst[:,:,0]) # [행,열,채널]
dst = cv2.cvtColor(dst, cv2.COLOR_YCrCb2BGR)

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()

 

 

10. CLAHE(Contrast Limited Adaptive Histogram Equalization)

- 평탄화를 하면 이미지의 밝은 부분이 날아가는 현상을 보정하기 위해 사용
- 이미지의 일정한 영역으로 나누어 평탄화를 적용하는 방법
    CLAHE객체 = cv2.createCLAHE(대비, 영역크기)
    CLAHE객체.apply(영상)   // apply함수를 사용해서 CLAHE객체에 영상을 적용

 

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('./field.bmp')
ycc = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb) # BGR을 YCrCb로 변환

# 밝기채널에 대해 equalize를 적용
img_eq = ycc.copy()
img_eq[:,:,0] = cv2.equalizeHist(img_eq[:,:,0])
img_eq = cv2.cvtColor(img_eq, cv2.COLOR_YCrCb2BGR) # YCrCb를 BGR로 변환


img_clahe = ycc.copy()
clahe = cv2.createCLAHE(clipLimit=4, tileGridSize=(4,4))
# clipLimit: 어느정도의 범위를 줄지를 결정(대비. 클수록 뚜렷하지만, 너무 크면 노이즈가 됨)
# tileGridSize: x by x 의 크기로 쪼개는 것을 의미
img_clahe[:,:,0] = clahe.apply(img_clahe[:,:,0])
img_clahe = cv2.cvtColor(img_clahe, cv2.COLOR_YCrCb2BGR) # YCrCb를 BGR로 변환

cv2.imshow('img', img)
cv2.imshow('img_eq', img_eq)
cv2.imshow('img_clahe', img_clahe)
cv2.waitKey()

 

 

11. 정규화(Normalization)

- 특정영역에 값이 쏠려있는 경우, 화질을 개선하고 이미지 간의 연산 시 서로 조건이 다른 경우 같은 조건으로 변경
    cv2.normalize(정규화이전영상, 이후영상, 정규화구간1, 구간2, 알고리즘선택플래그)
       * 정규화 구간2:구간 정규화가 아닌 경우는 사용하지 않음
       알고리즘선택플래그:
        cv2.NORM_MINMAX: 정규화 구간1 ~ 정규화 구간2를 구간으로 정규화시킴
        cv2.NORM_L1: 전체 합으로 나눔
        cv2.NORM_L2: 단위 벡터로 정규화
        cv2.NORM_INF: 최대값으로 나눔

 

import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread('./Hawkes.jpg', cv2.IMREAD_GRAYSCALE)


# 첫번째 방법 (직접 손으로 정규화)
img_norm1 = img.astype(np.float32)
img_norm1 = ((img_norm1 - img_norm1.min())*255 / (img_norm1.max() - img_norm1.min()))
img_norm1 = img_norm1.astype(np.uint8)
# (전체값-최소값)*255 / (최대값-최소값)


# 두번째 방법
img_norm2 = cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX)
# 만들어진 두번째의 영상이 없다면 None을 사용
hist = cv2.calcHist([img], [0], None, [256], [0,255])
hist_norm1 = cv2.calcHist([img_norm1], [0], None, [256], [0, 255])
hist_norm2 = cv2.calcHist([img_norm2], [0], None, [256], [0, 255])


cv2.imshow('img', img)
cv2.imshow('img_norm1', img_norm1)
cv2.imshow('img_norm2', img_norm2)
cv2.waitKey()

hists = {'hist':hist, 'hist_norm1':hist_norm1, 'hist_norm2':hist_norm2}
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()