Skip to main content

모양 감지

전처리

# 파일 열기
image_path = 'shapes.webp'
image = cv.imread(image_path)
# 흑백 이미지로 변환
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# 7x7 커널로 가우시안 블러링
blurred = cv.GaussianBlur(gray, (7, 7), 0)
# 이진화
th, bin = cv.threshold(blurred, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
# 보기
Image.fromarray(bin)

윤곽선 추출

검은 색 배경에서 하얀색 물체의 윤곽선을 추출

contours, hierarchy = cv.findContours(
bin.copy(), # 이미지, non-zero 픽셀을 객체로 간주
cv.RETR_EXTERNAL, # 윤곽선 검출 모드
cv.CHAIN_APPROX_SIMPLE) # 윤곽선 근사화 방법
# (SIMPLE: 꼭지점만, NONE: 모든 점)
  • contours: 검출된 윤곽선 좌표.
  • hierarchy: 윤곽선 계층 정보.
    • 1, N, 4 형태의 행렬
    • 마지막 차원은 [이전, 다음, 자식, 부모]를 나타냄(없으면 -1)

윤곽선 추출

  • 윤곽선 검출 모드
    • EXTERNAL: 바깥 윤곽선만 검출하여 리스트로
    • LIST: 계층 정보 없이 모든 윤곽선 검출
    • CCOMP: 2단계까지 계층 구조
    • TREE: 다단계 계층 구조
  • 근사화 방법
    • cv.CHAIN_APPROX_NONE : 윤곽점들의 모든 점
    • cv.CHAIN_APPROX_SIMPLE : 윤곽점들 단순화하고 끝점만
    • cv.CHAIN_APPROX_TC89_L1 : Teh_Chin 연결 근사 알고리즘 L1 버전을 적용
    • cv.CHAIN_APPROX_TC89_KCOS : Teh_Chin 연결 근사 알고리즘 KCOS버전을 적용

윤곽선 그리기

contoured_image = image.copy() # 이미지 복사
i = 2 # 2번 도형
color = 0, 255, 0 # green
thickness = 3 # 두께
show(cv.drawContours(contoured_image, contours, i, color, thickness))

이미지 모멘트

  • 이미지의 픽셀 분포에 대한 가중치 평균 m_ji = Σ_x,y x^j * y^i * I(x, y)
    • x: 점의 x좌표
    • y: 점의 y좌표
    • I(x, y): 점의 밝기
  • 응용
    • m₀₀: 면적(모든 점의 밝기의 합)
    • m₁₀/m₀₀: 무게 중심의 x좌표, m₀₁/m₀₀: 무게 중심의 y좌표
M = cv.moments(contours[i]) # 모멘트 계산
cX = int(M["m10"] / M["m00"]) # x좌표의 무게 중심
cY = int(M["m01"] / M["m00"]) # y좌표의 무게 중심

무게 중심 그리기

dst = image.copy()
center_color = 0, 255, 0 # green
no_stroke = -1 # 테두리 없음
radius = 7 # 반지름
show(cv.circle(dst, (cX, cY), radius, center_color, no_stroke))

Ramer–Douglas–Peucker 알고리즘

  • 윤곽선을 다각형으로 근사하기 위한 알고리즘
  • cv.approxPolyDP으로 사용할 수 있음
  • 허용오차를 벗어남 → 남김
  • 허용오차에 포함 → 지움

다각형으로 근사

c = contours[i] # 곡선
peri = cv.arcLength(c, True) # 둘레 길이(폐곡선 여부)
approx = cv.approxPolyDP(
c, # 곡선
0.02 * peri, # 허용 오차
True) # 폐곡선 여부
n = len(approx) # n각형
n

퀴즈

풀이

도형 개수

image = cv.imread('5shapes.jpg')
src = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
th, bin = cv.threshold(src, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
contours, hierarchy = cv.findContours(
bin.copy(), # 이미지, non-zero 픽셀을 객체로 간주
cv.RETR_EXTERNAL, # 윤곽선 검출 모드
cv.CHAIN_APPROX_SIMPLE) # 윤곽선 근사화 방법
# (SIMPLE: 꼭지점만, NONE: 모든 점)
len(contours)

도형 그리기

contoured_image = image.copy() # 이미지 복사
i = 1 # 2번 도형
color = 0, 255, 0 # green
thickness = 3 # 두께
show(cv.drawContours(contoured_image, contours, i, color, thickness))

면적 계산

for i in range(5):
length = cv.contourArea(contours[i])
print(i, length)

무게 중심 계산

M = cv.moments(contours[i]) # 모멘트 계산
cX = int(M["m10"] / M["m00"]) # x좌표의 무게 중심
cY = int(M["m01"] / M["m00"]) # y좌표의 무게 중심
cX, cY