Programming/python-computer vision

Matplotlib Colormaps로 OpenCV Contours 그리는 법

방황하는 데이터불도저 2023. 7. 19. 20:12

객체인식, 이미지분할 등 컴퓨터 비전 task들 중에서 가장 기본이 되는 이미지 처리로 이미지속 객체들의 Contours를 찾고, 이를 시각화시키는 작업을 자주하게 된다. 이미지 속에서 객체들의 경계선을 추출한 contours는 보통 cv2.drawContours로 그려주게 될텐데, 이 때 matplotlib에서 제공하는 colormaps의 색상을 어떻게 활용할 수 있는지 아래에서 확인해보자.

 

우선, 이미지의 Contour를 얻기 위해 아래의 코드를 실행시켜줍니다.

import cv2
image = cv2.imread('./data/IMG_2421.jpg')

# read an image and convert BGR image to RGB and GRAY image
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

# threshold → Binary Image → find Contours
ret, thresh = cv2.threshold(gray, 127, 255, 0)
contour_thresh, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("Number of Contours found with Canny edges = ", str(len(contour_thresh)))
# Number of Contours found with Canny edges =  4879

위의 코드로 얻어진 contour를 일단 간단하게 연두색으로 표시하여 시각화시켜보겠습니다.

import matplotlib.pyplot as plt

plt.figure(figsize=(30,16))
plt.subplot(131), plt.imshow(image), plt.title('Original Image', size=30), plt.axis('off')
plt.subplot(132), plt.imshow(thresh, cmap='gray'), plt.title('Threshold Image', size=30), plt.axis('off')
plt.subplot(133), plt.imshow(cv2.drawContours(np.copy(image), contour_thresh, -1, (0,255,0), 3)), plt.title('Contours with Threshold Image', size=30), plt.axis('off')

결과 이미지

이제, 본격적으로 contour색을 matplotlib colormap색으로 바꿔보겠습니다.

 

colormap의 스펙트럼에 있는 색을 가져오기 위해서는 plt의 cm함수를 사용하면 됩니다.

cm들 중에서도 여러가지의 colormap들이 있는데, 그 중에서 저는 cool, spring, PiYG를 사용해보겠습니다.

 

(Matplotlib의 Colormaps의 종류들은 아래의 더보기에서 확인할 수 있습니다.)

 

먼저 코드를 돌려 실행한 결과입니다.

n = len(contour_thresh)

cool_colors = plt.cm.cool(np.linspace(0,1,n))
spring_colors = plt.cm.spring(np.linspace(0,1,n))
piyg_colors = plt.cm.PiYG(np.linspace(0,1,n)) 

cool_image, spring_image, piyg_image = np.copy(image), np.copy(image), np.copy(image)

for i in range(n):
    cool_image = cv2.drawContours(cool_image, contour_thresh, i, 255*cool_colors[i], 3)
    spring_image = cv2.drawContours(spring_image, contour_thresh, i, 255*spring_colors[i], 3)
    piyg_image = cv2.drawContours(piyg_image, contour_thresh, i, 255*piyg_colors[i], 3)

plt.figure(figsize=(30, 16))
plt.subplot(131), plt.imshow(cool_image), plt.title('Cool Colormap', size=30), plt.axis('off')
plt.subplot(132), plt.imshow(spring_image), plt.title('Spring Colormap', size=30), plt.axis('off')
plt.subplot(133), plt.imshow(piyg_image), plt.title('PiYG Colormap', size=30), plt.axis('off')

이미지 결과

 

코드에 대한 설명입니다.

 

#1 : colormap의 스펙트럼에 있는 색상들을 contour개수(n)만큼 나누어서 리스트로 불러온다.

   - np.linspace() : 0부터1까지 n분의 1의 간격으로 쪼개진 수를 담아준다. (정규화 작업을 위한 것)

       (ex.

   - plt.cm.colormap()을 하게되면, colormap 스펙트럼을 0에서 1로 두고 n등분낸 후에 모든 위치의 색상을 리스트에 담아주게 됩니다. (0에서 255의 n등분을 내는 것 보다 0에서 1의 n등분이 쉽고, 간단하기 때문에 linspace를 활용하였습니다.)

n = len(contour_thresh)

# Continuous Colormap에서 n개의 discrete 색상 추출
cool_colors = plt.cm.cool(np.linspace(0,1,n))
spring_colors = plt.cm.spring(np.linspace(0,1,n))
piyg_colors = plt.cm.PiYG(np.linspace(0,1,n))   

# 만약 반대순서로 스펙트럼 색상을 가져오고싶다면, 아래의 코드 실행
# spring_colors = plt.cm.spring(np.linspace(0,1,n)[::-1])

 

#2 : for 구문을 통해 colormap으로부터 불러온 색상들을 하나씩 지정하여 contour를 그려준다.

    - drawContours(컨투어를 그려줄 이미지, contours list, 몇번째 contour를 그릴 것인지, contour 색상, contour 두께)

    - for 구문을 통해 첫번째부터 n번째까지 각 순서에 맞는 i번째 contour를 i번째 색상으로 그려줍니다.

# image에 여러개가 중첩해서 그려지지 않도록 복사
cool_image, spring_image, piyg_image = np.copy(image), np.copy(image), np.copy(image)

for i in range(n):
    cool_image = cv2.drawContours(cool_image, contour_thresh, i, 255*cool_colors[i], 3)
    spring_image = cv2.drawContours(spring_image, contour_thresh, i, 255*spring_colors[i], 3)
    piyg_image = cv2.drawContours(piyg_image, contour_thresh, i, 255*piyg_colors[i], 3)

#3 : 이미지 시각화를 통해 그려진 contour 색상 확인 

plt.figure(figsize=(30, 16))
plt.subplot(131), plt.imshow(cool_image), plt.title('Cool Colormap', size=30), plt.axis('off')
plt.subplot(132), plt.imshow(spring_image), plt.title('Spring Colormap', size=30), plt.axis('off')
plt.subplot(133), plt.imshow(piyg_image), plt.title('PiYG Colormap', size=30), plt.axis('off')

 

한가지 색으로만 표시했을 땐 알 수 없었던 contour가 그려지는 순서도 알 수 있고, 한가지 색이 지루할 때 보기에도 예쁘게 표현할 수 있게 활용해보시면 좋을 것 같습니다.

 

감사합니다.