logo

윤곽선 추출

전처리

import cv2 as cv
import numpy as np
from PIL import Image

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

전처리 다이어그램 1

윤곽선 추출

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

윤곽선 추출

윤곽선 검출 모드는 어떤 윤곽선을 반환할지, 근사화 방법은 윤곽선을 구성하는 점을 얼마나 보존할지 결정합니다.

  • cv.RETR_EXTERNAL: 바깥 윤곽선만 검출하여 리스트로 반환
  • cv.RETR_LIST: 계층 정보 없이 모든 윤곽선 검출
  • cv.RETR_CCOMP: 2단계까지 계층 구조 구성
  • cv.RETR_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))

윤곽선 그리기 이미지 1

이미지 모멘트

이미지 모멘트는 이미지의 픽셀 분포에 대한 가중치 평균입니다.

  • x: 점의 x좌표
  • y: 점의 y좌표
  • I(x, y): 점의 밝기
  • m00: 면적, 모든 점의 밝기의 합
  • m10 / m00: 무게 중심의 x좌표
  • m01 / m00: 무게 중심의 y좌표
M = cv.moments(contours[i])  # 모멘트 계산
cX = int(M["m10"] / M["m00"])  # x좌표의 무게 중심
cY = int(M["m01"] / M["m00"])  # y좌표의 무게 중심

이미지 모멘트 응용 이미지 1

무게 중심 그리기

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))

무게 중심 그리기 이미지 1

Ramer–Douglas–Peucker 알고리즘

  • 윤곽선을 다각형으로 근사하기 위한 알고리즘
  • cv.approxPolyDP으로 사용할 수 있음
  • 허용오차를 벗어나는 점은 남기고, 허용오차 안에 포함되는 중간 점은 제거함

Ramer–Douglas–Peucker 알고리즘 이미지 1

Ramer–Douglas–Peucker 알고리즘 이미지 2

Ramer–Douglas–Peucker 알고리즘 이미지 3

Ramer–Douglas–Peucker 알고리즘 이미지 4

다각형으로 근사

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

볼록 껍질 convex hull

  • 어떤 도형을 둘러싼 볼록한 다각형 찾기
src = cv.imread("convex.webp")  # 예제 데이터
dst = src.copy()
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
_, binary = cv.threshold(gray, 150, 255, cv.THRESH_BINARY_INV)  # 이진화
contours, hierarchy = cv.findContours(  # 윤곽선
    binary, cv.RETR_CCOMP, cv.CHAIN_APPROX_NONE)
for i in contours:
    hull = cv.convexHull(i, clockwise=True)  # 볼록 껍질 찾기(True: 시계 방향)
    cv.drawContours(dst, [hull], 0, (0, 0, 255), 2)

볼록 껍질 convex hull 이미지 1

배경 색을 이용해 자르기

src = cv.imread('nut.png')
bg_color = src[0, 0].astype(np.int16)  # 왼쪽 상단 픽셀 색상 (배경색으로 가정)
error = 10
low = np.clip(bg_color - error, 0, 255).astype(np.uint8)
high = np.clip(bg_color + error, 0, 255).astype(np.uint8)
mask = cv.inRange(src, low, high)  # 배경색과 유사한 색상 검출
mask_inv = cv.bitwise_not(mask)  # 배경색이 아닌 부분 검출
coords = cv.findNonZero(mask_inv)  # 실제 그림이 있는 좌표 추출
x, y, w, h = cv.boundingRect(coords)  # 사각형으로 경계 추출
trimmed = src[y:y + h, x:x + w]  # 실제 그림 부분만 자름

배경 색을 이용해 자르기 이미지 1

윤곽선 관련 기타 함수들

  • cv.contourArea 윤곽선이 감싸는 영역의 면적
  • cv.fitLine 주어진 점에 적합한 직선
  • cv.minEnclosingTriangle 주어진 점을 감싸는 최소 크기 삼각형
  • cv.boundingRect 주어진 점을 감싸는 최소 크기 사각형
  • cv.minAreaRect 주어진 점을 감싸는 최소 크기 회전된 사각형
  • cv.minEnclosingCircle 주어진 점을 감싸는 최소 크기 원
  • cv.fitEllipse 주어진 점을 감싸는 타원
  • cv.isContourConvex 볼록 여부
  • cv.convexityDefects 볼록 껍질에서 가장 안으로 들어간 점
Previous
필터링