영역 분할 및 추출 (Image Segmentation)
개요 및 목적
- 영상 전체를 픽셀 수준에서 분석하여 서로 다른 의미를 가지는 영역들로 나누는 과정.
- 영상 전체를 의미 있는 영역 단위로 쪼개어, 이후 처리의 정확성과 효율을 높이기 위한 핵심 전처리 단계.
- 불필요한 배경/노이즈를 제거하고 의미 있는 영역(전경)만 남겨 분석 대상을 단순화 할 수 있다.
종류 및 방법
- 픽셀 값 기반(Thresholding)
- Global/Local Thresholding 등 (2. 영상 이진화)
- 경계 기반(Boundary-based)
- Canny Edge + coutour 추출 등 (5-2. 엣지 검출 필터)
- 영역 기반(Region-based)
- Region Growing, Watershed 알고리즘 등
- 클러스터링 기반
- K-means Clustering, Mean-shift Segmentation 등 (K-Means)
- 딥러닝 기반
- FCN, U-Net, Deeplab 등
Watershed 방법
개요
- 영상처리에서 대표적인 영역 기반 분할 (Region-based Segmentation) 방법 중 하나로, 영상을 지형(terrain)으로 해석하여 픽셀 값을 고도로 보고 낮은 값은 계곡, 높은 값은 산봉우리로 간주함.
- 물을 계곡에서부터 흘려보내며 서로 만나는 경계를 경계선으로 사용하여 영역 분할하는 아이디어.
- 경계가 모호한 경우에도 정확한 경계 추출이 가능하나, 노이즈에 민감하여 지나치게 많이 분할되는 문제가 발생함.
- 이러한 문제를 해결하기 위해 전처리(스무딩, Morphology) 또는 Marker 기반 Watershed 방법을 사용한다.
방법
- 지역 최소값(Local minima) 찾기
- 영상에서 픽셀 값이 낮은 지점을 “계곡의 바닥”으로 간주 (그레이스케일)
- 이 지점들이 초기 마커(seed) 역할을 함
- 침수 시뮬레이션(Flooding)
- 물을 아주 천천히 올린다고 가정 (픽셀 값 기준으로 높이 증가)
- 각 최소값에서 시작된 물줄기가 주변 픽셀로 확장됨
- 영역 라벨링(Label propagation)
- 확장되는 물줄기(영역)는 각각 고유한 라벨을 가짐
- 인접 픽셀은 아직 물에 잠기지 않았다면 가장 가까운 영역의 라벨을 받음
- 경계 결정(When waters meet)
- 서로 다른 영역의 물줄기가 동시에 한 픽셀에 도달하면, 그 픽셀은 어떤 영역에도 속하지 않고 경계선(Watershed line) 으로 지정됨
- 결과
- 이미지가 여러 개의 catchment basin(집수 분지)으로 분할됨
- 경계선이 객체의 구분선 역할을 함
OpenCV 응용
1. 입력 이미지 전처리
- 입력 이미지를 이진화(thresholding)를 통해 바이너리 이미지로 변환.
입력 이미지 | 바이너리 이미지 |
---|---|
![]() | ![]() |
import cv2 as cv
# 이미지 불러오기
img = cv.imread('coins.png')
# 그레이스케일 변환
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 이진화
ret, thresh = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV+cv.THRESH_OTSU)
2. 모폴로지를 통한 노이즈 제거 및 전경/배경 구분
- 바이너리 영상에서 작은 점이나 구멍을 제거하기 위해 모폴로지
Opening
또는Closing
을 통해 노이즈 제거. - 모폴로지
Dilation
를 통해 확실한 배경 영역을 찾고,distanceTransform
을 통해 확실한 전경 영역을 찾는다. - 배경 영역과 전경 영역의 차이를 불확실한 영역이라 정의함.
# 노이즈 제거
kernel = np.ones((3,3),np.uint8)
opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel, iterations = 2)
# 확실한 배경 영역
sure_bg = cv.dilate(opening,kernel,iterations=3)
# 확실한 전경 영역
dist_transform = cv.distanceTransform(opening,cv.DIST_L2,5)
ret, sure_fg = cv.threshold(dist_transform,0.7*dist_transform.max(),255,0)
# 불확실한 영역 (가장자리)
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg,sure_fg)
Distance Transform
- 이진 이미지(Binary Image)를 입력으로 받아, 배경(0) 과 전경(255) 을 기준으로 전경 픽셀에서 가장 가까운 배경 픽셀까지의 거리를 계산하는 변환.
- 전경 내부에서 중심에 가까울수록 값이 크고, 경계선 근처일수록 값이 작아진다.
cv2.distanceTransform(src, distanceType, maskSize)
# - src : 입력 이진 영상
# - distanceType : 거리 계산 방식
# - cv2.DIST_L1
# - cv2.DIST_L2
# - cv2.DIST_C
# - maskSize : 거리 계산 시 사용하는 마스크 크기(3, 5 또는 c2.DIST_MASK_PRECISE)
- 결과 영상은
float32
형태로, 전경 픽셀에서 가장 가까운 배경 픽셀까지의 거리 값.
3. Marker 생성
connectedComponents
를 이용해 확실한 전경 이미지를 배경을 0으로 레이블하고, 다른 객체는 1부터 시작하는 정수로 레이블을 지정한다.watershed
알고리즘에서는 미확인 영역을 0으로 간주하므로 배경은 1, 객체는 2이상의 값들로 변환하고, 불확실한 영역을 0으로 설정한다.
# Marker 라벨링
ret, markers = cv2.connectedComponents(sure_fg)
# 라벨 값 보정: 배경은 1, 객체는 2 이상
markers = markers + 1
markers[unknown == 255] = 0
3. Watershed 적용
watershed
에 이미지와 마커 정보 전달 및 수행. (경계는 -1)
# Watershed 실행
markers = cv2.watershed(img, markers)
img[markers == -1] = [0, 0, 255] # 경계 표시 (빨간색)
참고
- OpenCV: Image Segmentation with Watershed Algorithm
- OpenCV: Interactive Foreground Extraction using GrabCut Algorithm
- OpenCV: Image Segmentation with Distance Transform and Watershed Algorithm
- [Part Ⅶ. Semantic Segment.. : 네이버블로그
- [6] 영상분할(Segmentation, 영상기반 처리기법) :: 공대남독
- [OpenCV][C++] 영상 분할 image.. : 네이버블로그