11장. Canny 와 Edge 비교
좋은 경계 검출의 조건
색칠 공부 책의 밑그림을 그리는 사람에게 세 가지를 부탁한다고 해 봅시다. 사과의 테두리를 빠뜨리지 말 것, 사과가 없는 곳에 쓸데없는 선을 긋지 말 것, 그리고 선을 실제 테두리 자리에 한 줄로만 그을 것. 하나라도 어기면 아이가 색칠하기 어려운 밑그림이 됩니다.
그림의 세 상자가 존 캐니가 1986년 논문에서 세운 세 가지 기준입니다. 초록 상자의 "잘 찾기"는 진짜 경계를 빠뜨리지 않으면서 헛것도 찾지 않는 것입니다. 다홍 상자의 "제자리에"는 찾은 선이 실제 경계 위치에 놓이는 것이고, 보라 상자의 "한 번만"은 경계 하나에 선이 한 줄만 그어지는 것입니다. 여기서 밑그림을 그리는 사람이 경계 검출 방법이고, 세 가지 부탁이 세 기준입니다.
도형 그림으로 세 기준을 어긴 모습을 만들어 보았습니다. 맨 왼쪽은 그림을 그리면서 저장한 정답 경계입니다. 둘째 칸은 기준값을 너무 높게(100, 300) 잡아 바탕과 밝기 차이가 작은 타원과 오른쪽 아래 네모를 통째로 빠뜨렸습니다. 셋째 칸은 노이즈 낀 그림에 블러 없이 Canny 를 걸어 바탕에 헛것이 가득합니다. 넷째 칸은 Sobel 기울기 크기에 임계값만 대어 선이 몇 칸 두께로 뭉툭합니다.
세 기준 견주기
| 기준 | 어기면 생기는 일 | 밑그림에 빗대면 | Canny 에서 맡는 단계 |
|---|---|---|---|
| 잘 찾기 | 경계를 빠뜨리거나 헛것을 찾는다 | 테두리가 비거나 낙서가 생긴다 | 블러(헛것 줄이기), 이력 임계값(빠뜨림 줄이기) |
| 제자리에 | 선이 실제 경계에서 비켜난다 | 사과 옆에 선이 그어진다 | 기울기 크기의 꼭대기 찾기 |
| 한 번만 | 선이 두껍거나 두 줄이 된다 | 테두리를 여러 번 덧그린다 | 비최대 억제 |
짧은 예시
import cv2
# 도형 그림과 그 정답 경계(두께 1)를 흑백으로 읽는다
gray = cv2.imread("ch11_set/shapes.png", cv2.IMREAD_GRAYSCALE)
answer = cv2.imread("ch11_set/shapes_edge.png", cv2.IMREAD_GRAYSCALE)
# Canny 로 경계를 찾는다. 50, 150 은 1차시 실습 5에서 쓴 두 기준값
edges = cv2.Canny(gray, 50, 150)
print("정답 경계 칸 비율:", round(float((answer > 0).mean()), 3))
print("Canny 경계 칸 비율:", round(float((edges > 0).mean()), 3))교안 PC 에서는 정답이 0.02, Canny 가 0.019 입니다. 경계는 그림의 2% 남짓밖에 되지 않는 가는 선이라는 것을 기억해 두세요. 이 사실이 뒤에서 성능을 잴 때 중요한 실마리가 됩니다.
주의할 점
- 세 기준은 서로 당깁니다. 빠뜨리지 않으려고 기준을 낮추면 헛것이 늘고, 헛것을 줄이려고 높이면 약한 경계를 빠뜨립니다. 한쪽만 보고 고르지 않습니다.
- "경계 칸 비율"이 정답과 비슷하다고 잘 찾은 것은 아닙니다. 엉뚱한 자리에 같은 수의 칸을 찾아도 비율은 같습니다. 자리까지 견주는 법은 "성능을 숫자로 재기" 절에서 배웁니다.