영상이나 이미지에서 물체의 외곽을 인식하거나 도형을 분리하기 위해서는 윤곽선(Contour)검출이 필요합니다.

OpenCV의 cv2.findContours() 함수는 이진 이미지에서 흰색 영역의 외곽선을 찾아 그 형태를 분석할 수 있게 해줍니다.

image.png

컨투어는 이진 이미지에서 흰색 영역의 외곽선을 찾습니다.

그러므로 grayscale 변환 후 이진화를 한 뒤에 색 반전을 하여 검출하려는 객체의 색상을 하얀색으로 바꿉니다.

코드로 구현하면 아래와 같습니다.

gray = cv2.cvtColor(src, cv2.COLOR_RGB2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary = cv2.bitwise_not(binary)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE

image.png

원본 이미지가 이미 단색 도형으로 구성되어 있어, Grayscale 변환 결과와 Binary 결과가 거의 동일하게 나타납니다.

image.png

그 후 색상 반전을 하면 이렇게 나옵니다.

코드로 구현해 봅시다.

import cv2
import numpy as np
import matplotlib.pyplot as plt

IMG_PATH = r"C:\\Users\\danny\\Desktop\\Contour.png"

# 1. 이미지 읽기
src = cv2.imread(IMG_PATH, cv2.IMREAD_COLOR)
if src is None:
    raise FileNotFoundError(f"이미지를 찾을 수 없습니다: {IMG_PATH}")

# 2. 그레이스케일 변환
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

# 3. 이진화
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 4. 반전 (검은 배경, 흰 물체)
binary_inv = cv2.bitwise_not(binary)

# 5. 컨투어 검출
contours, hierarchy = cv2.findContours(
    binary_inv,
    cv2.RETR_CCOMP,
    cv2.CHAIN_APPROX_NONE
)

print("총 컨투어 개수:", len(contours))
print("hierarchy shape:", hierarchy.shape)

# 6. 컨투어 시각화용 이미지
contour_vis = src.copy()
for i, cnt in enumerate(contours):
    cv2.drawContours(contour_vis, [cnt], -1, (0, 0, 255), 2)
    cv2.putText(
        contour_vis,
        str(i),
        tuple(cnt[0][0]),
        cv2.FONT_HERSHEY_SIMPLEX,
        0.7,
        (0, 255, 0),
        1
    )
    print(i, hierarchy[0][i])

# 7. 화면 표시용 리사이즈
h, w = gray.shape[:2]
max_side = 800
scale = min(max_side / max(h, w), 1.0)

def resize(img):
    if scale == 1.0:
        return img
    return cv2.resize(img, (int(w * scale), int(h * scale)), interpolation=cv2.INTER_AREA)

gray_s       = resize(gray)
binary_s     = resize(binary)
binary_inv_s = resize(binary_inv)
contour_s    = resize(contour_vis)

# BGR → RGB (matplotlib용)
contour_s_rgb = cv2.cvtColor(contour_s, cv2.COLOR_BGR2RGB)

# 8. matplotlib 2×2 배치
fig, axes = plt.subplots(2, 2, figsize=(10, 8))

axes[0, 0].imshow(gray_s, cmap="gray")
axes[0, 0].set_title("Gray")
axes[0, 0].axis("off")

axes[0, 1].imshow(binary_s, cmap="gray")
axes[0, 1].set_title("Binary")
axes[0, 1].axis("off")

axes[1, 0].imshow(binary_inv_s, cmap="gray")
axes[1, 0].set_title("Binary Inverted")
axes[1, 0].axis("off")

axes[1, 1].imshow(contour_s_rgb)
axes[1, 1].set_title("Contours + Index")
axes[1, 1].axis("off")

plt.tight_layout()
plt.show()

image.png

출력 결과