[OpenCV] 이미지 ROI 마스크 적용 방법들, 컬러 마스크 중복 적용 하기 (x연산, bitwise, copyTo, addWeighted)

2023. 2. 18. 17:01🧡 Programming/💻 Python

이미지를 플롯하다보면 다양한 방법으로 이미지 마스킹을 하고 싶을 때가 있다. 

 

특히 segmentation과 같은 마스크를 같이 이용하는 네트워크를 이용하다보면 실제 gt mask와 pred mask를 효율적으로 플롯할 방법을 고민하게 된다. 

 

매번 마스킹 방법을 검색하는 것이 너무 번거로워서 내가 자주 사용하는 마스킹 방법들을 총 망라해 보겠다. 

 

💡 bitwise_and

💡 곱하기 연산 (x)

💡 copyTo

💡 addWeighted

💡 컬러 마스크 / 중복 마스크 적용 

 

 

사용 마스크 이미지

사용한 이미지는 skimage에서 가져온 PIL이미지이므로 opencv에서 사용 할  수 있도록 numpy 배열로 바꾸고 이진화를 먼저 수행했다.

horse_mask = cv2.cvtColor(np.array(images[0]),cv2.COLOR_BGR2GRAY)
_,horse_mask = cv2.threshold(horse_mask,127,255,cv2.THRESH_BINARY)

plt.figure()
plt.imshow(horse_mask,cmap="gray")

 

 

💡 bitwise_and

가장 간단한 방법은 bitwise를 사용하는 방법이다. 

다만 이 방법을 사용하면 단점이 마스킹 하는 영역의 정보를 손실한다는 점이다. 

mask 이미지의 경우 1채널이미지다 보니 bitwise 연산을 위해서 src이미지와 같이 똑같은 3채널로 바꾸는 과정을 포함하였다. 

 

bitwise_and의 경우 두 이미지의 공통된 영역을 보여준다 

위에서 사용한 horse_mask의 이미지의 경우 배경이 255이고 말 영역이 0이기 때문에 배경 영역을 보여준다. 

반대로 마스크 영역을 보여주고 싶으면 마스크 영역을 반전시키면 된다. 

horse_mask = cv2.cvtColor(np.array(images[0]),cv2.COLOR_BGR2GRAY)
_,horse_mask = cv2.threshold(horse_mask,127,255,cv2.THRESH_BINARY)

src = cv2.resize(np.array(images[1]),(300,400))

horse_mask = cv2.resize(np.array(horse_mask),(300,400))
horse_mask = np.repeat(horse_mask[:,:,np.newaxis],3,-1)

dst = cv2.bitwise_and(src,horse_mask)
plt.figure()
ax1 = plt.subplot(1,2,1)
ax1.imshow(dst)

dst = cv2.bitwise_and(src,255-horse_mask)
ax2 = plt.subplot(1,2,2)
ax2.imshow(dst)

 

 

💡 곱하기 연산 (x)

사실 numpy array가 0,1로 이루어진 마스크라면 단순히 곱하기 연산만으로도 마스킹을 수행 할 수 있다.

horse_mask = cv2.cvtColor(np.array(images[0]),cv2.COLOR_BGR2GRAY)
_,horse_mask = cv2.threshold(horse_mask,127,255,cv2.THRESH_BINARY)

horse_mask = cv2.resize(np.array(horse_mask),(300,400))
horse_mask = np.repeat(horse_mask[:,:,np.newaxis],3,-1)/255
horse_mask = horse_mask.astype(int)

src = cv2.resize(np.array(images[1]),(300,400))
dst = src * horse_mask

plt.figure()
plt.imshow(dst)

 

💡 copyTo

마스크의 ROI 영역에 새로운 이미지를 적용하고싶을 때 사용 할 수 있는 방법이다. 

horse 모양의 mask에 새로운 이미지를 적용해보도록 하자. 

src = cv2.resize(np.array(images[1]),(300,400))
dst = cv2.resize(np.array(images[2]),(300,400))
horse_mask = cv2.resize(horse_mask,(300,400))

cv2.copyTo(src,horse_mask,dst)
plt.figure()
plt.imshow(dst)

 

💡 addWeighted

이미지를 합성하는 방법이다. 꼭 마스크 이미지를 합성하는 것이 아닌 경우에도 addWeighted를 사용 할 수 있지만 마스크를 투명하게 이미지에 적용해서 보고싶은 경우에 사용 할 수 있다. 개인적으로 bitwise보다는 이 방법이 좋은 것 같다. 

실제 이미지에 어떻게 마스크가 적용되었는지 보기 편한 방법이라고 생각한다. 

 

아래 코드에서 cv2.addWeighted의 인자 중에 alpha와 beta를 볼 수 있는데

각각 src 이미지와 horse_mask에 적용되는 파라미터이다. 이미지를 어느정도의 비율로 보존할 것인지를 결정하는 파라미터이다. 

horse_mask = cv2.cvtColor(np.array(images[0]),cv2.COLOR_BGR2GRAY)
_,horse_mask = cv2.threshold(horse_mask,127,255,cv2.THRESH_BINARY)

src = cv2.resize(np.array(images[1]),(300,400))

horse_mask = cv2.resize(np.array(horse_mask),(300,400))
horse_mask = np.repeat(horse_mask[:,:,np.newaxis],3,-1)

alpha = 0.5
beta = 0.5
dst = cv2.addWeighted(src,alpha,horse_mask,beta,0.0)

plt.figure()
plt.imshow(dst)

 

💡 컬러 마스크 / 중복 마스크 적용

 

마스크를 적용하다보면 마스크를 꼭 흑백으로 해야하나 

마스크의 색상 자체에 의미 부여를 하고 싶은 경우가 생길 수 있다. 예를 들면 빨간 마스크는 나쁜 영역.. 파란 마스크는 좋은 영역..ㅎ 

 

마스크에 색상을 적용하는 방법은 자체 함수를 작성하면 된다. 

 

아래 코드의 apply_mask를 보면 마스크가 1인 곳에 이미지와 컬러 색상의 비율을 조정하여 이미지에 적용한 것을 볼 수 있다. 위에 있는 예제들에서는 mask의 값이 0또는255이었으므로 horse_mask의 값을 255로 나누어주어 함수 조건에 맞도록 했다. 마스크의 값을 255로 나누지 않고 마스크 적용하는 조건을 1에서 255로 바꾸는것도 가능하다.

def apply_mask(img,mask,color,alpha=0.5):
  image = img.copy()
  for c in range(3):
    image[:,:,c] = np.where(mask==1,
                            image[:,:,c]*
                            (1-alpha) + alpha*color[c]*255,
                            image[:,:,c])
  return image

horse_mask = cv2.cvtColor(np.array(images[0]),cv2.COLOR_BGR2GRAY)
_,horse_mask = cv2.threshold(horse_mask,127,255,cv2.THRESH_BINARY)

src = cv2.resize(np.array(images[1]),(300,400))
horse_mask = cv2.resize(np.array(horse_mask),(300,400))
horse_mask = 1-horse_mask/255

dst = apply_mask(src,horse_mask,(1,0,0),alpha=0.5)

plt.figure()
plt.imshow(dst)

이렇게 고양이의 얼굴에 말 모양의 마스크가 빨간색으로 나타났다. 

 

그렇다면 빨간색 마스크와 파란색 마스크를 중복으로 적용해보자 .

딱히 사용할 마스크 이미지가 없어서 원 모양의 마스크를 사용하였다. 

아래 코드를 사용하면 고양이 사진에 두 종류의 마스크가 각각 다른 색상으로 표시된 것을 볼 수 있다.

각자 적용하는 실험의 성격에 따라서 유용하게 적용 할 수 있을것이라 생각한다.  

horse_mask = cv2.cvtColor(np.array(images[0]),cv2.COLOR_BGR2GRAY)
_,horse_mask = cv2.threshold(horse_mask,127,255,cv2.THRESH_BINARY)

src = cv2.resize(np.array(images[1]),(300,400))
horse_mask = cv2.resize(np.array(horse_mask),(300,400))
horse_mask = 1-horse_mask/255

circle_mask = np.zeros_like(horse_mask)
circle_mask = cv2.circle(circle_mask,(100,100),50,(255,255,255),-1)
circle_mask = circle_mask/255

dst = apply_mask(src,horse_mask,(1,0,0),alpha=0.4)
dst = apply_mask(dst,circle_mask,(0,0,1),alpha=0.4)
plt.figure()
plt.imshow(dst)