티스토리 뷰

Python

[OpenCV2] 이미지 연산

up_one 2025. 4. 10. 16:30

2025.04.05 - [Python] - [OpenCV 1] 이미지 실행 및 스레시홀딩

 

[OpenCV 1] 이미지 실행 및 스레시홀딩

OpenCv은 컴퓨터 비젼과 관련된 작업을 위한 강력하고 다양한 기능을 제공하는 Python의 이미지 전처리 라이브러리입니다. 주요 기능은 이미지의 기본적인 처리 작업(읽기 및 쓰기)과 필터링, 특징

up-one-coding.tistory.com

OpenCV 라이브러리와 numpy 연산을 활용하면 이미지 합성, 블렌딩, 이미지 간의 차이를 알 수 있습니다. 다만 주의해야 할 점은 연산 결과로 픽셀 값이 255가 넘거나 0보다 작아지면 이미지가 깨지는 상태가 발생하기 때문에 반드시 0에서 255 사이의 값이 출력되도록 처리를 진행해야 합니다.

1. mask을 이용한 누적할당과 비교연산

mask은 연산을 진행할 픽셀의 위치를 지정하는 역할을 합니다. mask의 값이 [1, 0]인 경우 0이 아닌 위치에 있는 픽셀만 연산을 진행한다고 볼 수 있습니다. 

#mask을 이용한 누적할당
a = np.array([[1,2]],dtype=np.uint8)   #np.uint8은 범위가 0~255이므로 255초과값부터 다시 0으로 계산
b = np.array([[10,20]],dtype=np.uint8)
mask = np.array([[1,0]],dtype=np.uint8)

#누적할당과의 비교 연산
cal1 = cv2.add(a,b,None,mask)
cal2 = cv2.add(a,b,b.copy(),mask)	##mask로 연산이 안된 자리를 b로 대체
cal3 = cv2.add(a,b,a.copy(),mask)   #mask로 연산이 안된 자리를 a로 대체

print(cal1)			#[[11  0]]
print(cal2)			#[[11  20]]
print(cal3)			#[[11  2]]

 

cal1의 결과는 1과 10만 덧셈을 진행하고 2와 20은 연산을 진행하지 않은 것을 볼 수 있습니다. 즉 mask 배열에 의해 1로 지정된 위치의 픽셀 값끼리만 연산이 진행된 것을 확인할 수 있습니다. 누적 할당은 연산을 진행하지 않은 부분에 b의 값을 넣어 출력되게끔 하는 것입니다. add 함수에 b가 아닌 b.copy()을 넣은 이유는 b의 값이 원본이 아닌 cal2값을 가지는 것을 방지하는 방법이 존재합니다.

 

2. 알파 블렌딩

알파 블렌딩은 제대로 된 이미지 합성을 위해서 가중치를 부여하는 방법입니다. 기존 방법처럼 단순히 두 이미지를 연산하면 픽셀 값이 깨지는 현상이 발생합니다.

이미지 합성 시 기존 연산의 문제

#numpy을 이용한 알파블렌딩
alpha=0.5
result = img1*alpha + img2*(1-alpha)
result = result.astype(np.uint8)    #소수점을 제거하고 값을 0~255로 만들기

#cv2을 이용한 알파블렌딩
result2 = cv2.addWeighted(img1, alpha, img2, 0.2,0)

imgs = {'result1':result,
       'result2':result2}
for i, (k,v) in enumerate(imgs.items()):
    plt.subplot(1,2,i+1),plt.axis('off'),plt.imshow(v[:,:,::-1]),plt.title(k)

 

비트와이즈 연산은 두 이미지의 비트 단위로 연산을 진행하며, 두 이미지를 합성할 때 특정 영역만 선택할 때 유용한 방법입니다.

#이미지 생성
img1 = np.zeros((200,400),dtype=np.uint8)
img2 = np.zeros((200,400),dtype=np.uint8)

#img slicing
img1[:,:200] = 255    #왼쪽은 검은색, 오른쪽은 흰색
img2[100:200,:] = 255  #위쪽은 검은색, 아래쪽은 흰색

#비트와이즈 연산
bitAnd = cv2.bitwise_and(img1,img2)
bitOr = cv2.bitwise_or(img1,img2)
bitXor = cv2.bitwise_xor(img1, img2)
bitNot = cv2.bitwise_not(img1)

imgs = {'img1':img1, 'img2':img2,
        'And':bitAnd, 'Or':bitOr,
       'Xor':bitXor, 'Not':bitNot}
fig = plt.figure(figsize=(10, 8))
for i, (title, img) in enumerate(imgs.items()):
    plt.subplot(3,2,i+1)
    plt.title(title)
    plt.imshow(img, 'gray')
    plt.xticks([]); plt.yticks([])

plt.show()

 

0이 검은색, 1이 흰색을 의미할 때, AND 연산을 진행하면 두 이미지의 흰색 부분이 겹치는 위치만 흰색으로 표시되는 것을 알 수 있습니다. XOR 연산은 두 비트가 다를 때 1을 반환하고, 같을 때 0을 반환하는 연산임을 확인할 수 있습니다.

비트와이즈 연산 결과

 

3. 두 이미지의 차이

openCV의 뺄셈 연산은 두 이미지 사이의 차이를 확인할 때 유용합니다. 다만 두 이미지 픽셀 값의 차이는 음수가 나올 수 있기 때문에 두 이미지의 차이를 절댓값으로 변환하는 것이 필요합니다.

#두 이미지의 차이
#두 이미지의 픽셀 값을 빼면 음수가 나올 수 있으므로, 절댓값을 취해야한다
img1 = cv2.imread('./drawing1.jpg')
img2 = cv2.imread('./drawing2.jpg')
img1_GRAY = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
img2_GRAY = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

#두 이미지의 절대값 차 연산
diff = cv2.absdiff(img1_GRAY, img2_GRAY)

#차이를 극대화하기 위한 스레시홀드 처리 및 컬러 변환
#픽셀 값이 1보다 큰 값은 255로 변환, 그렇지 않은 픽셀값은 0으로 변환
_,diff = cv2.threshold(diff,1,255,cv2.THRESH_BINARY)
diff_red = cv2.cvtColor(diff, cv2.COLOR_GRAY2BGR)
diff = cv2.cvtColor(diff, cv2.COLOR_GRAY2BGR)
diff_red[:,:,0] = 0

#변화를 빨강색으로 표시
spot = cv2.bitwise_xor(img2, diff_red) 

#출력
fig = plt.figure(figsize=(20, 16))
plt.subplot(221), plt.axis('off'), plt.imshow(img1), plt.title("img1")
plt.subplot(222), plt.axis('off'), plt.imshow(img2), plt.title("img2")
plt.subplot(223), plt.axis('off'), plt.imshow(diff), plt.title("diff")
plt.subplot(224), plt.axis('off'), plt.imshow(spot), plt.title("spot")
plt.show()

 

예시 이미지에 따르면, 오른쪽 대각선 위 점 4개가 img2에는 존재하지 않은 것을 볼 수 있습니다. 두 이미지의 차이를 구하는 연산과 그 차이를 강조하기 위한 쓰레시홀딩 기법을 활용하면 위와 같은 결과처럼 차이를 명확하게 확인할 수 있습니다.

 

4. 이미지 합성

두 이미지(전경, 배경)를 합성하기 위해서는 mask 기법을 활용해야 합니다. mask을 이용해서 전경 이미지만 남기고, mask_inv을 이용해서 배경 이미지의 해당 영역을 남기는 것입니다. 이 영역은 전경이 들어갈 영역만 투명하게 처리하는 것입니다.

img_bg = cv2.imread('./dog.jpg')
img_fg = cv2.imread('./opencv_logo.png',cv2.IMREAD_UNCHANGED)

#알파채널을 이용해서 마스크와 역마스크 생성
_,mask = cv2.threshold(img_fg[:,:,3],1,255,cv2.THRESH_BINARY)
mask_inv = cv2.bitwise_not(mask)

#이미지 크기로 배경 사진에서 관심영역 잘라내기
img_fg = cv2.cvtColor(img_fg, cv2.COLOR_BGRA2BGR)
h,w = img_fg.shape[:2]
roi = img_bg[10:10+h, 10:10+w]     #전경을 합성할 위치

#mask을 이용해서 오려내기
masked_fg = cv2.bitwise_and(img_fg, img_fg, mask=mask)
masked_bg = cv2.bitwise_and(roi,roi,mask=mask_inv)

#이미지 합성
added = masked_fg+masked_bg
img_bg[10:10+h, 10:10+w] = added

#이미지 color맵 발생
mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2RGB)
added = cv2.cvtColor(added, cv2.COLOR_BGR2RGB)

fig = plt.figure(figsize=(10, 8))
plt.subplot(231), plt.axis('off'), plt.imshow(mask), plt.title("mask")
plt.subplot(232), plt.axis('off'), plt.imshow(added), plt.title("added")
plt.show()

전경과 합성의 결과
합성 결과

TAG more
글 보관함
최근에 올라온 글