OpenCV - 기본 이미지 연산

2 분 소요

AND, OR, NOT, XOR

이미지에 and, or, not, xor 연산이 가능하다. 이때, 이미지 연산은 각 픽셀의 b,g,r 값에 대해 색이 없으면 검게 보이고(0) 색이 있으면 흰색(1)으로 0과 1로만 구분하여 비트연산을 수행한다.

AND (cv2.bitwise_and)

두 이미지에서 모두 흰색(1)인 부분만 흰색으로 나타난다.

True and True == True / True and False == False / False and False == False 와 같은 원리

img1 = cv2.imread('1.jpg')
img2 = cv2.imread('2.jpg')

img3 = cv2.bitwise_and(img1, img2)

1

왼쪽의 이미지와 중앙의 이미지를 AND로 연산한 결과가 오른쪽의 이미지. 공통적으로 흰색인 부분만 흰색으로 나타나고 나머지는 검은색으로 처리되었다.

OR (cv2.bitwise_or)

두 이미지 중에서 한 쪽에서만 흰색(1)이더라도 결과적으로 흰색으로 나타난다. 다르게 말하면, 두 이미지에서 모두 검은색(0)인 부분만 검은색으로 나타난다.

img4 = cv2.bitwise_or(img1, img2)

2

NOT (cv2.bitwise_not)

이미지의 0과 1이 반대로 되어 나타난다.

img5 = cv2.bitwise_not(img2)

3

XOR (cv2.bitwise_xor)

두 이미지에서 값이 서로 같으면 검은색(0) 같지 않으면 흰색(1)으로 나타난다.

img6 = cv2.bitwise_xor(img1, img2)

4

이미지 합성

픽셀값을 이용한 합성

두 이미지의 픽셀값을 더하여 합성한다. 이 때 그냥 더할 경우 포화문제가 발생하므로 포화연산을 추가하여 합성을 진행한다.

img1 = cv2.imread('a.jpg', 1)
img2 = cv2.imread('b.jpg', 1)
# img3 = img1 + img2
# 그냥 픽셀값을 더할수도 있지만 포화문제를 막기위해 아래연산 추가
    
img1 = img1.astype('int16')
img2 = img2.astype('int16')
img3 = np.clip(img1+img2, 0, 255)

img3 = img3.astype('uint8')

함수를 이용한 합성

이미지 합성 함수를 이용하면 아주 간단하게 픽셀 연산과 똑같은 결과를 얻을 수 있다.

img3 = **cv2.add(img1, img2)**

가중치 합성

이미지 합성시 어떤 이미지를 더 강하게 나오게 할지 가중치를 주어 연산한다. 이때 가중치의 합은 1이어야 하고 가중치가 큰 쪽의 이미지가 더 강하게 나온다.

dst(x, y) = alpha * src1(x, y) + beta * src2(x, y)

img1 = img1.astype('int64')
img2 = img2.astype('int64')

img3 = np.clip(0.4*img1 + 0.6*img2, 0, 255)

img3 = img3.astype('uint8')

img1에는 가중치를 0.4를 주고, img2에는 가중치 0.6을 줬기 때문에 결과물에서는 img2가 더 강하게 보여진다.

가중치 합성 함수를 사용하면 더 간편하게 연산이 가능하다.

# cv2.addWeighted(img1, 가중치1, img2, 가중치2, 결과물에 더할값(밝기조절))
img3 = cv2.addWeighted(img1, 0.4, img2, 0.6, 0)

마스크 연산

이미지가 겹쳐져서 합성되는 것을 원하지 않고, 기존 이미지 위에 새 이미지가 그대로 얹어지길 원할 경우 마스크 연산을 하면 된다.

얹을 이미지의 불필요한 부분의 값을 0 으로 만들고, 얹어질 이미지의 얹을 이미지 영역 값도 0으로 만든 뒤 합성을 하면 픽셀값이 바뀌지 않으므로 두 이미지의 원본 모습 그대로 합칠 수 있게된다.

logo = cv2.imread('logo.png', 1)
img = cv2.imread('a.jpg', 1)
h, w, c = logo.shape
roi = img[150:150+h, 150:150+w]  # logo를 넣을 img 영역

mask = cv2.cvtColor(logo, cv2.COLOR_BGR2GRAY)  # logo를 흑백처리
#이미지 이진화
mask[mask[:]==255]=0  # 배경을 검정으로
mask[mask[:]>0]=255  # 검정이 아닌 글자의 픽셀값들을 흰색으로

mask_inv = cv2.bitwise_not(mask)  # mask반전 => 배경은 흰색. 글자는 검정

# 마스크(검정배경, 흰색글자)와 로고 칼라이미지 and 하여 컬러로고만 추출
roi_fg = cv2.bitwise_and(logo, logo, mask=mask)
# roi와 반전마스크(배경흰색, 글자검정)와 and하여 roi에 로고 모양만 검정색으로 됨
roi_bg = cv2.bitwise_and(roi, roi, mask=mask_inv)

dst = cv2.add(roi_fg, roi_bg)  # 로고 글자와 글자모양이 뚤린 배경을 합침

img[150:150+h, 150:150+w] = dst  # roi를 제자리에 넣음

이미지의 차

빼기 연산을 통해 두 이미지의 차를 구할 수 있다. 마찬가지로 포화연산이 필요하다.

수식은 다음과 같으며,

  • dst(x, y) = src1(x, y) - src2(x, y)

차함수를 사용해서 연산할 수 있다.

  • cv2.subtract(img2, img1)

하지만 위의 차 함수를 사용할 경우 검정색(0)에서 빼면 음수가 되어 결과적으로 모두 검은색이 되고 (포화연산), 흰색에서 빼면 보색(색상반전)이 되므로 절대값으로 처리하는 것이 더 자연스럽다.

  • cv2.absdiff(img2, img1)

영상에서 움직이는 객체 추출

차함수를 활용하면 영상에서 움직이는 객체(변화가 생기는 값)만 추출하는 것이 가능하다

import cv2
cap = cv2.VideoCapture(0)  # 카메라 오픈

# 카메라 사이즈 정의
cap.set(3, 300) 
cap.set(4, 200)

prev_frame = None
ret, prev_frame = cap.read()#첫 영상 읽음

while True:
    ret, frame = cap.read() 
    if ret:  #정상 읽기일 때만
        f = cv2.absdiff(prev_frame, frame)  # 연속해서 영상의 차를 연산
        cv2.imshow('img', f)  # 이전 프레임과의 차로 이루어진 영상을 윈도우에 출력
        prev_frame = frame
    k = cv2.waitKey(1)
    if k==27: #입력한 키가 esc이면
        break

cap.release()
cv2.destroyAllWindows()

댓글남기기