이미지 비트연산

이미지는 AND, NOT, OR, XOR 의 비트연산이 가능하다. 이미지 비트연산은 이미지에서 특정 영역을 추출하거나 직사각형 모양이 아닌 ROI를 정의할 때, 이미지에서 바탕을 제거하고 2개의 이미지를 합칠 때에 유용하게 사용할 수 있다. 이때, 중요한 것은 비트 연산할 이미지의 크기가 동일해야 한다는 것이다.

def bitOperation_black_bg(hpos, vpos):
    img1 = cv2.imread('iceland-1979445_1280.jpg')
    img2 = cv2.imread('opencv_logo.png')
    
    rows, cols, channels = img2.shape
    roi = img1[ vpos:vpos + rows, hpos:hpos + cols ]
    
    img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
    ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)
    mask_inv = cv2.bitwise_not(mask)
    
    img1_bg = cv2.bitwise_and(roi, roi, mask = mask_inv)
    img2_fg = cv2.bitwise_and(img2, img2, mask = mask)
    
    dst = cv2.add(img1_bg, img2_fg)
    img1[ vpos:vpos + rows, hpos:hpos + cols ] = dst
    
    b, g, r = cv2.split(img1)
    img1 = cv2.merge([r, g, b])
    
    plt.imshow(img1)
    plt.title('result')
    plt.xticks([])
    plt.yticks([])
    plt.show()
bitOperation_black_bg(10, 10)

 

def bitOperation_white_bg(hpos, vpos):
    img1 = cv2.imread('iceland-1979445_1280.jpg')
    img2 = cv2.imread('atom-4.png')
    
    rows, cols, channels = img2.shape
    roi = img1[ vpos:vpos + rows, hpos:hpos + cols ]
    
    img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
    ret, mask = cv2.threshold(img2gray, 203, 255, cv2.THRESH_BINARY)
    mask_inv = cv2.bitwise_not(mask)
    
    img1_bg = cv2.bitwise_and(roi, roi, mask = mask)
    img2_fg = cv2.bitwise_and(img2, img2, mask = mask_inv)
    
    dst = cv2.add(img1_bg, img2_fg)
    img1[ vpos:vpos + rows, hpos:hpos + cols ] = dst
    
    b, g, r = cv2.split(img1)
    img1 = cv2.merge([r, g, b])
    
    plt.imshow(img1)
    plt.title('result')
    plt.xticks([])
    plt.yticks([])
    plt.show()
bitOperation_white_bg(10, 20)

위 두 함수는 로고의 배경이 검은색인지, 흰색인지에 따라서 달라진다. 특별한 차이점은 없으나 black_bg 에서는 img1_bg 변수를 초기화 할 때 mask = mask_inv로 지정해준 반면 white_bg 에서는 img2_bg 변수를 초기화 할 때 mask = mask로 지정해준 것이 차이점이다.

black_bg 함수부터 차근차근살펴보자

 


def bgr_to_rgb(imgfile):
    b, g, r = cv2.split(imgfile)
    imgfile = cv2.merge([r, g, b])
    return imgfile

openCV에서 컬러 이미지를 나타낼 때, 각각의 채널은 순서대로 B, G, R을 나타낸다. 다만 python의 matplotlib로 이미지를 읽을 때는 채널을 각각 R, G, B 순으로 판단하여 읽어, 이미지의 채널의 순서를 변경하지 않은 상태로 matplotlib를 이용해 이미지를 읽을 경우 색이 이상하게 나타내어질 수도 있다. 이를 방지하기 위해 해당 함수를 만들어 이미지 파일의 색 순서를 바꿔보자.

img1 = cv2.imread('iceland-1979445_1280.jpg')
img2 = cv2.imread('opencv_logo.png')

imgfile1_rgb = bgr_to_rgb(img1)
imgfile2_rgb = bgr_to_rgb(img2)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.imshow(imgfile1_rgb)
plt.title('imgfile1')
plt.xticks([])
plt.yticks([])

plt.subplot(1, 2, 2)
plt.imshow(imgfile2_rgb)
plt.title('imgfile2')
plt.xticks([])
plt.yticks([])

plt.tight_layout()
plt.show()

우선 배경이 될 이미지를 img1, 그 이미지에 집어넣을 다른 로고 이미지를 img2에 지정한다.

이후 로고 이미지를 배경 이미지에 넣을 위치를 ROI를 통해 지정해준다. 우선 img2.shape를 통해 각각의 요소를 rows, cols, channels에 저장한다. 그리고 배경 이미지에 그 크기만큼 관심영역을 지정해준다. 여기에 black_bg 함수의 hpos와 vpos 인자가 들어가게 되는데 임시로 10으로 지정해주었다.

rows, cols, channels = img2.shape
roi = img1[ 10:10 + rows, 10:10 + cols ]

roi_bgr = bgr_to_rgb(roi)
plt.imshow(roi_bgr)
plt.title('ROI')
plt.xticks([])
plt.yticks([])
plt.show()

먼저 로고 이미지를 흑백으로 변환하고 이를 binary image로 전환한다. 이때 threshold 함수가 사용되는데 이에 대해서는 나중에 자세하게 다룰 예정이다. 간단하게 설명하면, img2gray 이미지에서 픽셀값이 10이상 넘어가는 부분을 255(흰색)으로 바꿔주는 부분이다. 그리고 해당 이미지를 mask에 저장한다. 비트연산 NOT을 이용해 이를 반전시켜 mask_inv에 저장한다. 즉, mask_inv에서는 mask에서 흰색인 부분이 검은색으로 저장된다.

img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)
mask_inv = cv2.bitwise_not(mask)

images = [img2gray, mask, mask_inv]
titles = ["gray-scale", "mask", "mask_inv"]

plt.figure(figsize=(8, 8))
for i in range(3):
    plt.subplot(1, 3, i+1)
    plt.imshow(images[i], cmap="gray")
    plt.title(titles[i])
    plt.axis('off')
    
plt.tight_layout()
plt.show()

cv2.bitwise_and 연산에서는 AND 연산이 수행된다. 즉 둘 다 0이 아닌 경우에만 값을 통과시킨다는 뜻이다. 이때 mask를 지정해주면 mask가 0이 아닌 부분에서만 AND 연산을 수행한다. mask_inv는 로고부분이 0(검정색)이고, mask는 로고가 아닌 배경부분이 0(검정색)이므로 AND연산을 수행하였을 때 img1_bg는 로고부분만 0(검은색), img2_fg는 배경부분만 0(검은색)으로 나오게 된다. 따라서 이 둘을 add 함수로 연산하였을 때 최종적으로 합쳐지게 된다.

img1_bg = cv2.bitwise_and(roi, roi, mask = mask_inv)
img2_fg = cv2.bitwise_and(img2, img2, mask = mask)
dst = cv2.add(img1_bg, img2_fg)

img1_bg_rgb = bgr_to_rgb(img1_bg)
img2_fg_rgb = bgr_to_rgb(img2_fg)
dst_rgb = bgr_to_rgb(dst)

images = [img1_bg_rgb, img2_fg_rgb, dst_rgb]
titles = ["img1_bg", "img2_fg", "dst"]

plt.figure(figsize=(8, 8))
for i in range(3):
    plt.subplot(1, 3, i+1)
    plt.imshow(images[i], cmap="gray")
    plt.title(titles[i])
    plt.axis('off')
    
plt.tight_layout()
plt.show()

dst를 다시 관심영역으로 지정했던 부분에 합쳐주게 되면

img1[ 10:10 + rows, 10:10 + cols ] = dst
cv2.imshow('result', img1)
cv2.waitKey(0)
cv2.destroyAllWindows()

최종적으로 배경 이미지에 로고를 합성한 이미지가 나타나게 된다.

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기