OpenCV 비전 실무12장 · Contour Detection 과 객체 분석11 / 21← 이전목차다음 →MetaContents

12장. Contour Detection 과 객체 분석

개수 세기와 잡티 걸러 내기

콩을 고르는 할머니는 체에 콩을 담아 흔듭니다. 흙이나 작은 돌가루는 체 구멍으로 빠지고, 콩만 남습니다. 남은 콩을 한 줌씩 세면 몇 알인지 압니다. 이진화한 사진에도 먼지나 노이즈가 만든 작은 흰 점이 섞여 있습니다. 윤곽선의 면적을 체로 삼아 작은 것을 버리면 물체만 남습니다.

이진화, 찾기, 면적 재기, 거르기, 세기의 다섯 단계 흐름도

그림의 다섯 단계를 차례로 밟습니다. 어두운 부품을 Otsu INV 로 흰색으로 만들고, EXTERNAL 로 윤곽선을 찾고, 윤곽선마다 면적을 재고, 기준보다 작으면 버린 뒤, 남은 목록의 길이로 개수를 셉니다. 여기서 체의 구멍 크기가 기준 면적, 빠져나가는 흙이 잡티, 남은 콩이 물체입니다.

작업대 사진, Otsu INV 이진화, 모든 윤곽선, 면적 100 이상만 남긴 윤곽선을 나란히 놓은 그림

가상의 작업대 사진입니다. 이진화하면 부품 사이에 먼지가 흰 점으로 남아, 윤곽선이 51개나 잡힙니다(셋째 칸, 다홍). 면적 100 이상만 남기면 부품 12개가 남습니다(넷째 칸, 초록).

기준 면적 정하기

무엇교안 PC 의 면적(contourArea)기준과의 관계
먼지(반지름 1)2기준보다 훨씬 작다
먼지(반지름 2)8기준보다 훨씬 작다
붙어 버린 먼지 두 개16기준보다 작다
가장 작은 부품(반지름 8)198기준보다 크다

기준은 가장 큰 잡티(16)보다 크고 가장 작은 부품(198)보다 작게, 그 사이의 넉넉한 값 100 으로 골랐습니다. 두 무리의 사이가 넓을수록 기준을 고르기 쉽습니다.

connectedComponentsWithStats 와 견주기

findContours 와 connectedComponentsWithStats 로 덩어리를 세는 두 길을 견준 비교 그림

9장에서 흰 덩어리의 수를 세던 cv2.connectedComponents 에는 통계까지 주는 형제 함수가 있습니다. 초록 상자의 findContours 는 테두리 점 목록을 주고 면적과 상자는 따로 재야 합니다. 다홍 상자의 connectedComponentsWithStats 는 칸마다 덩어리 번호를 매기고, 덩어리마다 상자, 면적(흰 칸 수), 무게중심을 한 번에 줍니다.

견줄 것findContoursconnectedComponentsWithStats
돌려주는 것윤곽선 목록, hierarchy덩어리 수, 번호 그림, 통계표, 무게중심
바탕세지 않는다0번으로 센다(개수에서 1 을 뺀다)
면적점을 이은 넓이(조금 작다)흰 칸의 수
구멍TREE 로 따로 찾는다면적에서 저절로 빠진다
더 할 수 있는 일회전 상자, 외접원, 13장의 모양 특징번호 그림으로 덩어리 칸만 골라 내기

통계표(stats)는 덩어리마다 한 줄이고 칸은 다섯 개입니다.

칸상수뜻
0cv2.CC_STAT_LEFT상자의 왼쪽 x
1cv2.CC_STAT_TOP상자의 위쪽 y
2cv2.CC_STAT_WIDTH상자의 가로
3cv2.CC_STAT_HEIGHT상자의 세로
4cv2.CC_STAT_AREA흰 칸의 수(면적)

짧은 예시

Python
import cv2

gray = cv2.imread("ch12_tray.png", cv2.IMREAD_GRAYSCALE)
# 부품이 어두우므로 INV 로 뒤집어 부품을 흰색으로. Otsu 가 기준값을 고른다
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
parts = [c for c in contours if cv2.contourArea(c) >= 100]   # 면적 100 이상만 남긴다
print("윤곽선:", len(contours), "→ 거른 뒤:", len(parts))

# connectedComponentsWithStats: (덩어리 수 + 바탕 1, 번호 그림, 통계표, 무게중심)
n, labels, stats, centroids = cv2.connectedComponentsWithStats(binary)
big = (stats[1:, cv2.CC_STAT_AREA] >= 100).sum()           # 0번(바탕)을 빼고 센다
print("덩어리:", n - 1, "→ 거른 뒤:", int(big))

교안 PC 에서는 윤곽선 51개 → 12개, 덩어리 51개 → 12개로 두 길의 개수가 같습니다.

주의할 점

  • 기준 면적은 사진의 해상도에 따라 다릅니다. 같은 부품도 사진을 두 배로 키우면 면적은 네 배가 됩니다. 사진 크기가 바뀌면 기준도 다시 정합니다.
  • 잡티가 부품에 붙어 있으면 면적으로 거를 수 없습니다. 그때는 9장의 열기로 먼저 떼어 냅니다.
  • 그림 가장자리에 걸쳐 잘린 물체도 윤곽선이 잡힙니다. 크기를 재는 검사라면 상자가 그림 끝에 닿은 물체는 따로 표시합니다.
← 이전 절12장 · 물체를 감싸는 상자와 원다음 절 →12장 · 분석 결과를 표로 정리하기