OpenCV(3)

현서·2025년 8월 8일

컴퓨터 비전

목록 보기
6/16
post-thumbnail

모폴로지 연산(Morphological Operations)

  • 이진화된 영상에서 객체의 형태를 조작하는 연산
    침식(Erosion), 팽창(Dilation), 열기(Open), 닫기(Close) 등

  • 주로 노이즈 제거, 객체의 형태 보정, 경계 강조, 윤곽선 추출 등에 사용된다.

침식(Erosion) : 객체를 축소하여 작은 노이즈를 제거하는 데 사용
팽창(Dilation) : 객체를 확장하여 빈 공간을 채우는 역할
열기(Open) : 작은 노이즈를 효과적으로 제거하는 데 사용
닫기(Close) : 객체의 내부 구멍을 메우는 데 유용


25_morphology.py

import cv2
import numpy as np

# 모폴로지 연산
# 영상 처리에서 형태(Shape) 기반으로 이미지를 다루는 기법
# 특히 이진 영상, 흑백 영상에서 물체의 외곽선 크기, 형태를 변경하거나 분석하는데 사용

# 팽창
# 밝은 영역을 넓히는 연산
# 흰색 물체가 커짐, 구멍이 메워짐
# 얇은 부분이 굵어짐

# 침식
# 밝은 영역을 줄이는 연산
# 흰색 물체가 작아짐
# 가느다란 연결부가 끊어짐, 노이즈가 제거됨

# 열림
# 침식 후 팽창: 작은 노이즈를 제거한 후 얇은 부분이 굵어짐

# 닫힘
# 팽창 후 침식: 구멍이 메워짐

img = cv2.imread('./images/circuit.bmp', cv2.IMREAD_GRAYSCALE)
# MORPH_RECT: 사각형, MORPH_ELLIPSE: 타원, MORPH_CROSS: 십자

# gse = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# gse = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# gse = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 3))
gse = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))

dst1 = cv2.dilate(img, gse)
dst2 = cv2.erode(img, gse)

cv2.imshow('img', img)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()

확실히 dst1(팽창) 은 흰색 부분이 굵어졌고, dst1(침식) 은 흰색 부분이 얇아진 모습을 확인할 수 있었다.

MORPH_RECT: 사각형, MORPH_ELLIPSE: 타원, MORPH_CROSS: 십자
이 세가지 중 MORPH_RECT: 사각형을 가장 많이 사용한다고 한다.
3가지 모양에 따라 결과가 조금씩 달라지는 것을 확인할 수 있다.

항목MORPH_RECTMORPH_ELLIPSEMORPH_CROSS
연산 강도강함중간약함
속도/효율빠름 (단순 구조)약간 느림빠름
방향성모든 방향부드러운 방향성수직/수평 강조
사용 빈도가장 높음특정 상황드물게 사용

26_labeling.py

import cv2

# 라벨링
# 이미지 속에서 연결된 픽셀 덩어리를 찾아서 각각에 번호를 붙이는 작업
# 이진 영상에서 사용되며, 물체별로 분리, 개수 세기, 위치 분석 등에 활용

# 라벨링 과정
# 1. 이진화: 흑백으로 변환
# 2. 픽셀 그룹 찾기: 상, 하, 좌, 우 방향으로 연결된 그룹, 대각선까지 포함한 그룹
# 3. 각 그룹에 라벨 번호를 부여
# 4. 각 객체의 위치, 크기, 면적, 중심점 등을 구함

img = cv2.imread('./images/keyboard.bmp', cv2.IMREAD_GRAYSCALE) # grayscale에 색상있는 사각형 못 그림
_, img_bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
dst = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

# connectedComponentsWithStats: 한 번에 라벨링과 통계값 계산
# cnt: 찾은 객체 개수(배경 포함)
# labels: 각 픽셀의 라벨 번호가 저장된 2D 배열
# stats: 각 객체의 [x, y, width, height, area] 정보, area: 픽셀 개수
# centroids: 각 객체의 중심 좌표(부동소수점 값)
cnt, labels, stats, centroids = cv2.connectedComponentsWithStats(img_bin)
print(cnt)
print('-' * 50)
print(labels)
print('-' * 50)
print(stats)
print('-' * 50)
print(centroids)
print('-' * 50)

for i in range(1, cnt):
    (x, y, w, h, area) = stats[i]
    if area < 20:
        continue
    cv2.rectangle(dst, (x, y, w, h), (0, 255, 255)) # 노랑색 사각형

cv2.imshow('img_bin', img_bin)
cv2.imshow('dst', dst)
cv2.waitKey()

if area < 20: 의 의미
area는 픽셀 개수인데, 픽셀 개수가 20보다 작은 것들 노이즈일 것으로 취급한다!!!

노이즈 아닌 객체들만 잘 잡아낸 모습을 볼 수 있었다.


27_contour.py

import cv2
import random
import numpy as np

# 외곽선
# 영상에서 같은 색 또는 강도를 가진 경계선을 추적해 얻는 좌표 집합
# 영상 분석에서 물체의 모양, 크기, 위치를 찾을 때 유용

img = cv2.imread('./images/contours.bmp', cv2.IMREAD_GRAYSCALE)
milkdrop = cv2.imread('./images/milkdrop.bmp', cv2.IMREAD_GRAYSCALE)
_, img_bin = cv2.threshold(milkdrop, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
h, w = milkdrop.shape[:2]
dst_milk = np.zeros((h, w, 3), np.uint8)

# img: 이진 이미지 사용
# mode: 컨투어 추출 방식
# RETR_CCOMP: 2계층 구조로 모든 컨투어 추출
# RETR_LIST: 모든 컨투어 추출
# RETR_EXTERNAL: 최외곽 컨투어만 추출
# RETR_TREE: 모든 컨투어를 트리 구조로 추출(복잡한 계층 정보 포함)

# method: 컨투어 근사화 방식
# CHAIN_APPROX_NONE: 모든 경계 좌표 저장(정밀하지만 데이터 큼)
# CHAIN_APPROX_SIMPLE: 직선 구간은 시작/끝만 저장(효율적)

# contours: 외곽선의 좌표 목록(리스트)

# hierarchy: 계층 관계를 나타내는 배열
# hierarchy[0][i] = [next, prev, child, parent]
# next: 같은 계층 레벨에서 다음 컨투어의 인덱스(없으면 -1)
# child: 하위(내부) 컨투어의 인덱스(없으면 -1)
# parent: 상위(외부) 컨투어의 인덱스(없으면 -1)
contours, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
milk_coutours, _ = cv2.findContours(img_bin, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
# print(contours)
# print(hierarchy)

dst = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
# 전체 외곽선을 한 번에 그림
cv2.drawContours(dst, contours, -1, color, 3)
# -1: → 모든 외곽선(contours)을 한꺼번에 그리겠다는 의미
# color: 1개의 랜덤 컬러를 전체 외곽선에 적용
# 3: 선 두께 (굵은 선)

for i in range(len(milk_coutours)):
    color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    cv2.drawContours(dst_milk, milk_coutours, i, color, 2)
    # i: 인덱스를 하나씩 증가시켜서 각 외곽선 개별 처리
    # 매 반복마다 color를 새로 지정 → 각 외곽선마다 다른 색
    # 2: 선 두께 (약간 얇음)

cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.imshow('dst_milk', dst_milk)
cv2.waitKey()


28_polygon.py

import cv2
import math

def setLabel(img, pts, label):
    (x, y, w, h) = cv2.boundingRect(pts)
    pt1 = (x, y)
    pt2 = (x + w, y + h)
    cv2.rectangle(img, pt1, pt2, (0, 0, 255), 2)
    # pt1: 좌상단 꼭짓점
    # pt2: 우하단 꼭짓점
    # (0, 0, 255): 빨간색 (BGR)
    # 2: 선 두께 2px
    cv2.putText(img, label, pt1, cv2.FONT_HERSHEY_DUPLEX, 1, (0, 0, 255))

img = cv2.imread('./images/polygon.bmp')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# THRESH_BINARY_INV: 임계값 이상이면 검정(0), 임계값 미만이면 흰색(255)
_, img_bin = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
contours, _ = cv2.findContours(img_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

for pts in contours:
    if cv2.contourArea(pts) < 50:
        continue
    # approxPolyDP : 꼭짓점 개수를 줄여 간단한 규칙 기반 분류
    approx = cv2.approxPolyDP(pts, cv2.arcLength(pts, True) * 0.02, True)
    vtc = len(approx)
    print(vtc)

    if vtc == 3:
        setLabel(img, pts, 'TRI')
    elif vtc == 4:
        setLabel(img, pts, 'RECT')
    else:
        length = cv2.arcLength(pts, True)
        area = cv2.contourArea(pts)
        # 4 * math.pi * area / (length * length): 원형도를 구함
        # 값의 범위: 0 ~ 1, 1에 가까울수록 원에 가까움
        ratio = 4. * math.pi * area / (length * length)
        if ratio > 0.8:
            setLabel(img, pts, 'CIR')
        else:
            setLabel(img, pts, 'NONAME')

cv2.imshow('img', img)
cv2.waitKey()

approx = cv2.approxPolyDP(pts, cv2.arcLength(pts, True) * 0.02, True)

  • pts (curve)
    원래 외곽선 (점들의 배열)
    보통 cv2.findContours()에서 얻은 외곽선 중 하나 (contour[i])
  • cv2.arcLength(pts, True) * 0.02
    (= epsilon : "얼마나 단순화할지"를 결정하는 파라미터)
    arcLength()는 외곽선의 둘레 길이를 계산함
    * 0.02: 허용 오차 비율 (2%)
    → 이 값이 작을수록 더 정밀하게 근사
    → 클수록 꼭짓점 수가 줄어들고 단순화
  • True (closed)
    True: 외곽선이 닫힌 도형이라고 가정
    보통 외곽선은 닫혀 있으므로 True 사용
  • arcLength(pts, True): 외곽선의 둘레 길이 계산
  • contourArea(pts): 외곽선이 감싸는 면적 계산


profile
The light shines in the darkness.

0개의 댓글