OpenCV 비전 실무11장 · Canny 와 Edge 비교14 / 21← 이전목차다음 →MetaContents

11장. Canny 와 Edge 비교

실습 1 — cv2.Canny 기본과 두 기준값 격자

도형 그림(ch11_set/shapes.png)에 cv2.Canny 를 걸어 결과의 꼴을 보고, 1장의 과일 진열대(fruit_stand.png)에서 두 기준값을 여러 개로 바꿔 경계 칸 비율을 표와 격자 그림으로 견줍니다. 마지막에 찾은 경계를 컬러 그림 위에 겹쳐 봅니다.

1단계. 그림 읽고 Canny 걸기

Python
import cv2
import numpy as np

# 도형 그림과 정답 경계를 흑백으로 읽는다. 연습용으로 그려 만든 가상의 그림이다
gray = cv2.imread("ch11_set/shapes.png", cv2.IMREAD_GRAYSCALE)
answer = cv2.imread("ch11_set/shapes_edge.png", cv2.IMREAD_GRAYSCALE)
print("모양:", gray.shape, "/ 자료형:", gray.dtype)

# cv2.Canny(그림, threshold1, threshold2): 작은 쪽이 low, 큰 쪽이 high
edges = cv2.Canny(gray,    # image: 8비트 흑백 그림
                  50,      # threshold1: low. 이보다 작은 칸은 버린다
                  150)     # threshold2: high. 이 이상인 칸은 강한 경계
# 결과: 0 과 255 두 값만 있는 이진 그림
print("결과에 있는 값:", [int(v) for v in np.unique(edges)])
print("Canny 경계 칸 비율:", round(float((edges > 0).mean()), 3))
print("정답 경계 칸 비율:", round(float((answer > 0).mean()), 3))
출력
모양: (240, 320) / 자료형: uint8
결과에 있는 값: [0, 255]
Canny 경계 칸 비율: 0.019
정답 경계 칸 비율: 0.02

2단계. 과일 진열대에서 두 기준값을 바꿔 가며 표 만들기

Python
import pandas as pd

# 1차시 실습 5에서 맛보기로 Canny 를 걸었던 가상의 과일 진열대 그림
fruit = cv2.imread("fruit_stand.png")
fruit_gray = cv2.cvtColor(fruit, cv2.COLOR_BGR2GRAY)

lows = [25, 50, 100]
highs = [100, 200, 400]
rows, results = [], {}
for low in lows:
    for high in highs:
        e = cv2.Canny(fruit_gray, low, high)
        results[(low, high)] = e
        rows.append({"low": low, "high": high, "경계 칸 비율": round(float((e > 0).mean()), 4)})

table = pd.DataFrame(rows)
# pivot: low 를 줄, high 를 칸으로 펼친 표로 바꾼다
print(table.pivot(index="low", columns="high", values="경계 칸 비율"))
출력
high     100     200     400
low
25    0.0324  0.0245  0.0187
50    0.0323  0.0244  0.0187
100   0.0233  0.0195  0.0144

3단계. 격자 그림으로 견주기

Python
import matplotlib.pyplot as plt

# 그래프의 한글이 깨지지 않게 맑은 고딕을 쓰고, 빼기 기호가 네모로 나오지 않게 한다
plt.rcParams["font.family"] = "Malgun Gothic"
plt.rcParams["axes.unicode_minus"] = False

fig, axes = plt.subplots(3, 3, figsize=(10, 7.6))
for i, low in enumerate(lows):
    for j, high in enumerate(highs):
        axes[i, j].imshow(results[(low, high)], cmap="gray", vmin=0, vmax=255)
        axes[i, j].set_title(f"low {low}, high {high}")
        axes[i, j].axis("off")
plt.tight_layout()
plt.show()
과일 진열대 그림에 low 25·50·100 과 high 100·200·400 을 짝지어 건 아홉 가지 Canny 결과를 3행 3열로 놓은 격자

4단계. 찾은 경계를 컬러 그림 위에 겹쳐 보기

Python
# 1차시와 같은 기준값 50, 150 으로 다시 건다
edges = cv2.Canny(fruit_gray, 50, 150)
# 원본을 복사해 경계 칸만 빨간색(B, G, R = 0, 0, 255)으로 칠한다
overlay = fruit.copy()
overlay[edges > 0] = (0, 0, 255)
print("경계 칸 비율:", round(float((edges > 0).mean()), 3))

fig, axes = plt.subplots(1, 2, figsize=(10, 3.8))
axes[0].imshow(edges, cmap="gray", vmin=0, vmax=255)
axes[0].set_title("Canny (50, 150)")
axes[1].imshow(cv2.cvtColor(overlay, cv2.COLOR_BGR2RGB))
axes[1].set_title("원본 위에 겹친 경계")
for ax in axes:
    ax.axis("off")
plt.tight_layout()
plt.show()
출력
경계 칸 비율: 0.028
과일 진열대의 Canny 결과와, 원본 위에 경계를 빨간색으로 겹친 그림

코드 설명

  • cv2.Canny(gray, 50, 150) 은 0 과 255 두 값만 있는 경계 그림을 돌려줍니다. 깨끗한 도형 그림에서 경계 칸 비율은 0.019 로 정답 경계(0.02)와 거의 같습니다.
  • 표를 가로로 읽으면 high 의 효과입니다. low 25 에서 high 를 100, 200, 400 으로 올리면 경계 칸 비율이 0.0324, 0.0245, 0.0187 로 줄어듭니다. high 를 넘는 강한 경계가 줄면 거기에 이어질 약한 선도 함께 사라지기 때문입니다. 격자의 오른쪽 줄(high 400)에서는 가운데 귤의 위쪽 테두리와 무늬가 사라졌습니다.
  • 표를 세로로 읽으면 low 의 효과입니다. low 25 와 50 은 0.0324 와 0.0323 으로 거의 같고, 100 이면 0.0233 으로 줄어듭니다. 격자의 맨 아래 줄(low 100)에서는 진열대 판자의 옅은 가로 줄무늬가 사라집니다.
  • overlay[edges > 0] = (0, 0, 255) 는 경계 칸만 빨간색으로 칠합니다. OpenCV 는 B, G, R 순서라 (0, 0, 255) 가 빨강입니다. 경계 칸 비율 0.028 은 1장 실습 5에서 본 값과 같습니다.
← 이전 절11장 · 실습 지도다음 절 →11장 · 실습 2 — Canny 의 단계 따라가기