OpenCV - 기본 이미지 연산
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)
왼쪽의 이미지와 중앙의 이미지를 AND로 연산한 결과가 오른쪽의 이미지. 공통적으로 흰색인 부분만 흰색으로 나타나고 나머지는 검은색으로 처리되었다.
OR (cv2.bitwise_or)
두 이미지 중에서 한 쪽에서만 흰색(1)이더라도 결과적으로 흰색으로 나타난다. 다르게 말하면, 두 이미지에서 모두 검은색(0)인 부분만 검은색으로 나타난다.
img4 = cv2.bitwise_or(img1, img2)
NOT (cv2.bitwise_not)
이미지의 0과 1이 반대로 되어 나타난다.
img5 = cv2.bitwise_not(img2)
XOR (cv2.bitwise_xor)
두 이미지에서 값이 서로 같으면 검은색(0) 같지 않으면 흰색(1)으로 나타난다.
img6 = cv2.bitwise_xor(img1, img2)
이미지 합성
픽셀값을 이용한 합성
두 이미지의 픽셀값을 더하여 합성한다. 이 때 그냥 더할 경우 포화문제가 발생하므로 포화연산을 추가하여 합성을 진행한다.
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()
댓글남기기