12장. Contour Detection 과 객체 분석
면적과 둘레
울타리를 치고 그 안에 잔디를 까는 일을 떠올려 봅시다. 울타리가 몇 미터인지는 둘레이고, 잔디가 몇 장 드는지는 넓이입니다. 같은 윤곽선에서 이 두 가지를 잽니다. 넓이는 cv2.contourArea, 둘레는 cv2.arcLength 입니다.
초록 상자의 cv2.contourArea 는 윤곽선의 점을 이어 만든 도형의 넓이를 칸의 넓이 단위로 돌려줍니다. 다홍 상자의 cv2.arcLength 는 점을 이은 선의 길이를 칸 단위로 돌려줍니다. 여기서 울타리가 윤곽선, 울타리 길이가 둘레, 잔디 장수가 면적입니다.
가로 4칸, 세로 3칸의 흰 덩어리는 흰 칸이 12개입니다. 그런데 윤곽선의 점(다홍)은 테두리 칸의 가운데에 놓이므로, 점을 이은 도형(보라)은 가로 3, 세로 2 로 넓이가 6 입니다. 칸의 바깥쪽 절반씩이 넓이에서 빠집니다. 비유와 다른 점은 이것입니다. 울타리는 잔디 바깥에 치지만, 윤곽선은 잔디의 가장자리 칸 한가운데를 지나갑니다.
손으로 계산해 보기
도형 판의 곧은 네모는 가로 80칸, 세로 50칸으로 칠했고, 원은 반지름 35 로 칠했습니다.
| 도형 | 흰 칸의 수 | 손계산 | contourArea | arcLength |
|---|---|---|---|---|
| 네모 80×50 | 80 × 50 = 4,000 | 점 사이는 79 × 49 = 3,871 | 3871.0 | 2 × (79 + 49) = 256 |
| 원 반지름 35 | 3,853 | 3.14 × 35 × 35 = 3,846.5 | 3754.0 | 2 × 3.14 × 35 = 219.8 보다 길게 230.8 |
네모의 contourArea 는 점 사이의 거리(79, 49)로 계산한 3,871 과 꼭 같습니다. 원은 흰 칸이 3,853 개로 πr²(3,848.5)와 거의 같지만, contourArea 는 가장자리 칸의 바깥 절반이 빠져 3,754 로 조금 작습니다. 둘레는 원의 테두리가 계단 모양이라 대각선 걸음이 섞여 손계산보다 조금 길게 나옵니다.
짧은 예시
import cv2
shapes = cv2.imread("ch12_shapes.png", cv2.IMREAD_GRAYSCALE)
contours, _ = cv2.findContours(shapes, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
rect = contours[-1] # 곧은 네모(그린 차례에서 마지막)
# contourArea: 점을 이은 도형의 넓이
print("네모 면적:", cv2.contourArea(rect))
# arcLength(윤곽선, closed): True 면 마지막 점과 첫 점을 이어 닫힌 둘레를 잰다
print("닫힌 둘레:", cv2.arcLength(rect, True))
print("열린 길이:", cv2.arcLength(rect, False))교안 PC 에서는 면적 3871.0, 닫힌 둘레 256.0, 열린 길이 177.0 입니다. 열린 길이는 마지막 점 (99, 20) 에서 첫 점 (20, 20) 으로 돌아오는 한 변(79)을 빼고 49 + 79 + 49 만 잽니다. 윤곽선의 둘레를 잴 때는 늘 True 를 줍니다.
주의할 점
- 크기가 작은 덩어리일수록 contourArea 와 흰 칸 수의 차이가 커집니다. 반지름 2 짜리 점은 흰 칸 13개인데 contourArea 는 8 입니다. 아주 작은 물체를 견줄 때는 흰 칸 수를 쓰는 "개수 세기와 잡티 걸러 내기" 절의 방법도 함께 봅니다.
- 면적과 둘레의 단위는 칸입니다. 실제 크기(mm)로 바꾸려면 자처럼 크기를 아는 물체를 함께 찍어 "한 칸이 몇 mm" 인지 알아야 합니다.
- RETR_EXTERNAL 로 찾은 도넛의 면적에는 구멍이 들어 있습니다. 구멍을 뺀 넓이는 TREE 로 구멍의 윤곽선을 찾아 그 면적을 빼서 구합니다(실습 2).