Segmentation & Connected Component Analysis

인화·2026년 6월 17일

영상처리

목록 보기
6/9

Segmentation

영상 분할(Segmentation)은 이미지를 여러 개의 영역(Region)으로 나누는 작업을 의미한다. 즉, 이미지 안에서 특정 객체(Object)와 배경(Background)을 구분하거나, 색상, 밝기, 텍스처 등이 유사한 픽셀들을 하나의 영역으로 묶는 과정이다. 이렇게 분할된 영역은 객체 인식, 추적, 압축, 분석 등 다양한 후속 처리에서 활용된다.

영상 분할의 목적은 이미지 전체를 하나의 덩어리로 보는 것이 아니라, 의미 있거나 유사한 특징을 가진 영역 단위로 나누어 다루는 것이다. 예를 들어 동전 이미지에서는 동전과 배경을 구분할 수 있고, 풍경 이미지에서는 하늘, 나무, 도로처럼 비슷한 색상이나 질감을 가진 영역을 나눌 수 있다.

분할 기법에는 여러 가지가 있으며, 여기서는 대표적으로 세 가지 방법을 살펴본다. 첫 번째는 K-means 기반 영상 분할이다. K-means는 각 픽셀을 색상 또는 특징 벡터로 보고, 비슷한 픽셀끼리 같은 군집으로 묶는 방법이다. 두 번째는 Graph-based Segmentation이다. 이 방법은 픽셀을 그래프의 노드로 보고, 픽셀 간 유사도나 가중치를 이용하여 비슷한 픽셀끼리 하나의 영역으로 분할한다. 세 번째는 Watershed 알고리즘이다. Watershed는 이미지를 밝기값에 따른 높이 지형처럼 보고, 마커(Marker)를 기준으로 영역을 확장시키면서 서로 다른 영역이 만나는 경계를 찾는 방법이다.

정리하면, 영상 분할은 이미지에서 의미 있는 영역을 나누기 위한 전처리 또는 분석 과정이며, K-means, Graph-based, Watershed와 같은 다양한 방법을 통해 객체와 배경, 또는 유사한 영역들을 구분할 수 있다.

K-means 군집 기반 영상 분할

K-means 알고리즘은 데이터를 K개의 클러스터로 나눈다. 이는 이미지의 각 픽셀을 하나의 데이터로 보고, 픽셀의 색상값을 기준으로 비슷한 픽셀끼리 묶는 방법이다. 컬러 이미지에서는 보통 각 픽셀을 (R, G, B) 또는 (B, G, R) 색상 벡터로 표현한다.

K-means의 목표 함수(오차 제곱합)는 다음과 같다.

!image.png

  • xix_i : i번째 픽셀의 색상(혹은 특징) 벡터

  • μkμ_k : k번째 군집의 중심 (평균)

  • wikw_{ik} : i번째 픽셀이 k번째 군집에 속하면 1, 아니면 0

    K-means는 먼저 k개의 군집 중심을 정한 뒤, 각 픽셀을 가장 가까운 군집 중심에 할당한다. 그다음 각 군집에 속한 픽셀들의 평균값으로 군집 중심을 다시 계산한다. 이 과정을 반복하면서 픽셀들이 k개의 색상 군집으로 나뉘게 된다. 즉, K-means 영상 분할은 이미지를 k개의 대표 색상으로 단순화하는 과정이라고 볼 수 있다. 원본 이미지의 모든 픽셀은 자신이 속한 군집의 중심 색상으로 바뀌므로, 결과 이미지는 색상이 단순화되고 비슷한 색을 가진 영역끼리 묶인다.

K-means 알고리즘의 단계
1. 무작위(또는 K-means++ 방식)로 초기 중심 μkμ_k 설정
2. 각 데이터(픽셀)를 현재 군집 중심 중 가장 가까운 곳에 할당
3. 군집별로 평균(중심)을 다시 계산
4. 수렴할 때까지 2~3단계 반복

영상 분할에서의 K-means

  • 영상의 모든 픽셀 (xix_i)을 색상 벡터로 간주

  • K개의 색상 클러스터로 분할

  • 결과적으로 원본 영상이 K개의 대표 색상으로 양자화(Quantization) 됨.

  • 서로 다른 위치지만 색이 유사하면 같은 군집으로 묶이는 것이 특징임.
    ** 양자화 : 많고 다양한 값을 몇 개의 대표값으로 줄이는 것.

    하지만 K-means는 기본적으로 색상 정보만 보고 분할한다. 따라서 서로 멀리 떨어진 위치에 있어도 색이 비슷하면 같은 군집으로 묶일 수 있고, 반대로 같은 물체라도 조명이나 그림자 때문에 색이 다르면 다른 군집으로 나뉠 수 있다.

    이때 군집의 수인 k 값이 증가하면 군집의 개수가 많아진다. 즉, 이미지를 표현하는 대표 색상의 수가 많아지므로 원본 이미지의 색상과 세부 정보가 더 많이 보존된다. 하지만 너무 큰 k를 사용하면 이미지가 지나치게 잘게 나뉘어 과분할이 발생할 수 있고, 작은 색상 차이까지 다른 영역으로 분리될 수 있다.

k 값결과
작음색상이 단순화되고 큰 영역 중심으로 분할됨
더 많은 색상과 세부 영역이 표현됨
너무 큼작은 차이까지 분리되어 과분할 가능성 증가

Graph-based Segmentation

Graph-based Segmentation은 그래프 이론을 기반으로 이미지를 여러 영역으로 나누는 분할 기법이다. 이 방법에서는 이미지를 하나의 그래프로 표현한다. 각 픽셀은 그래프의 노드(Node)가 되고, 서로 인접한 픽셀 사이의 관계는 엣지(Edge)가 된다. 이때 엣지에는 두 픽셀이 얼마나 비슷한지를 나타내는 가중치가 부여된다.

픽셀 간 유사도는 보통 색상 차이, 밝기 차이, 위치 인접도 등을 기준으로 계산된다. 유사도가 높은 픽셀들은 같은 영역으로 묶이고, 차이가 큰 픽셀들 사이에는 경계가 형성된다. 즉, Graph-based Segmentation은 픽셀 간의 유사도와 경계 강도를 함께 고려하여 이미지를 여러 개의 세그먼트로 나누는 방식이다.

이 과정에서는 Minimum Spanning Tree나 최소 컷과 같은 그래프 이론의 개념이 사용될 수 있다. 비슷한 픽셀끼리는 연결을 유지하고, 차이가 큰 픽셀 사이의 연결은 끊어 서로 다른 영역으로 분리한다. 이러한 Graph-based Segmentation은 K-means와 달리 단순히 색상만 보는 것이 아니라, 인접한 픽셀 간의 관계를 함께 고려한다. 그래서 이미지 안에서 비슷한 색과 연결성을 가진 영역을 나누는 데 적합하다.

이러한 Graph-based Segmentation는cv.ximgproc.segmentation.createGraphSegmentation를 통해 구현되어 있으며, 주요 파라미터는 다음과 같다.

  • sigma는 분할 전에 이미지를 얼마나 부드럽게 만들지 결정한다. 값이 크면 노이즈가 줄어들어 작은 잡음에 의한 분할은 줄어들지만, 세밀한 경계가 흐려질 수 있다.
  • k는 분할 정도를 조절하는 파라미터이다. k가 작으면 픽셀 간 작은 차이도 경계로 판단하기 쉬워서 세그먼트가 많이 생긴다. 반대로 k가 크면 웬만한 차이는 같은 영역으로 묶이기 때문에 분할 수가 줄어든다.
  • min_size는 너무 작은 세그먼트를 제거하기 위한 값이다. 분할 결과에서 min_size보다 작은 영역은 주변의 다른 영역과 합쳐진다. 따라서 작은 잡음 영역을 줄이는 데 도움이 된다.
파라미터의미값이 커지면
sigma가우시안 블러 정도 (노이즈 감소)노이즈가 줄고 더 부드럽게 분할됨
k픽셀 간 경계를 나누는 유사도 기준값이 크면 분할이 덜 일어나고 큰 영역으로 묶임
min_size분할된 영역의 최소 픽셀 수작은 영역이 큰 영역에 병합됨

Watershed 알고리즘

Watershed 알고리즘은 이미지를 지형처럼 생각하는 분할 방법이다. 영상을 지형(높이)으로 보고, 물이 고이는 과정을 시뮬레이션하며, 물이 합류하는 경계선을 찾는 기법이다. 다시 말해, 밝기값이나 gradient 값을 높낮이로 보고, 낮은 지점에서 물이 차오른다고 가정한다. 물이 차오르면서 서로 다른 물웅덩이가 만나게 되는 지점이 경계가 된다. 이 경계를 이용해 객체를 분리한다.

Watershed를 이해하기 위해서는 먼저 Marker의 개념을 알아야 한다. Marker는 Watershed 알고리즘이 영역 확장을 시작하는 기준점이다. 즉, 확실한 물체 내부인 전경(Foreground)과 확실한 배경(Background)을 미리 라벨링한 정보를 의미한다. 예를 들어 동전 이미지에서는 동전 내부가 확실한 전경 marker가 되고, 동전이 아닌 부분은 확실한 배경 marker가 된다. 반면 동전의 경계 근처처럼 전경인지 배경인지 명확하지 않은 부분은 unknown 영역으로 둔다.

Watershed 알고리즘은 이미지를 높낮이가 있는 지형처럼 보고, 각 marker에서 물이 차오르듯이 영역을 확장시킨다. 이때 서로 다른 marker에서 확장된 영역이 만나게 되는 지점이 객체의 경계가 된다. OpenCV에서는 이러한 경계 부분을 -1 라벨로 표시한다.

따라서 Marker는 Watershed의 결과를 결정하는 중요한 요소이다. Marker가 정확하게 설정되면 객체와 배경의 경계가 잘 분리되지만, Marker가 잘못 설정되면 하나의 객체가 여러 개로 나뉘거나 여러 객체가 하나로 합쳐지는 문제가 발생할 수 있다. 예를 들어 객체 내부 marker가 잘못 잡히면 하나의 객체가 여러 개로 쪼개질 수 있고, 배경 marker가 잘못 잡히면 배경이 객체 안으로 침범할 수 있다. 따라서 Watershed의 성능은 전처리와 marker 품질에 크게 의존한다.

Watershed의 기본 동작 과정은 다음과 같다.
1. 이미지를 grayscale로 변환한다.
2. 이진화와 morphology 연산을 통해 노이즈를 제거한다.
3. 확실한 배경 영역을 찾는다.
4. distance transform 등을 이용해 확실한 전경, 즉 객체 내부 영역을 찾는다.
5. 전경 영역에 connected component labeling을 적용해 marker를 만든다.
6. 배경, 전경, 불확실한 영역을 구분한 marker를 입력으로 사용한다.
7. Watershed를 수행하면 marker들이 주변으로 확장된다.
8. 서로 다른 marker가 만나는 지점은 경계가 되며, 이 경계를 -1 라벨로 표시한다.

Watershed의 수식적 아이디어는 다음과 같다. 이때, 여러 개의 marker가 있고, 각 marker는 어떤 물체 내부나 배경을 대표하는 시작점이다. 이미지 안의 어떤 픽셀 x가 있을 때, 그 픽셀이 어느 marker에 가장 가까운지 보고, 픽셀 x가 가장 가까운 marker의 라벨을 상속받는다. (Voronoi-like 방식) 즉, 각 픽셀이 가장 가까운 marker의 라벨을 따라가고, 서로 다른 marker의 영역이 만나는 지점을 경계로 정한다.

Connected Component Analysis (연결 요소 분석)

CCA의 개념

Connected Component Analysis, CCA는 이진 이미지(binary image)에서 서로 연결된 전경 픽셀들을 하나의 객체로 묶어 라벨을 부여하는 방법이다. Threshold나 segmentation을 하면 보통 전경 픽셀은 흰색으로, 배경 픽셀은 검은색으로 결과가 나온다. 하지만, 이 상태에서는 그저 흰색 픽셀들이 있을 뿐이지 이 픽셀들이 몇 개의 물체인지, 각 물체가 어디에 있고 얼마나 큰지, 노이즈인지 진짜 객체인지를 구분할 수 없다. 그래서 CCA를 수행해 연결된 전경 픽셀들을 하나의 객체로 묶어 단순한 binary mask를 객체 목록으로 바꾸는 방법을 활용한다.

CCA는 이미지에서 전경 픽셀들을 검사하면서 서로 연결되어 있는 픽셀들을 같은 component로 판단한다. 예를 들어 동전 이미지에서 각 동전이 서로 떨어져 있다면, CCA는 각 동전을 서로 다른 객체로 라벨링할 수 있다. 이렇게 하면 이미지 안에 객체가 몇 개 있는지, 각 객체가 어디에 위치하는지, 크기가 얼마나 되는지 등을 알 수 있다.

이러한 연결 요소 분석은 보통 아래 흐름으로 사용된다. 이때 중요한 점은 connectedComponentsWithStats()는 이미 이진화된 이미지를 입력으로 받는다는 것이다. 즉, CCA의 성능은 대부분 “얼마나 좋은 binary mask를 만들었는가?”에 의해 결정된다.

입력 이미지
   ↓
Grayscale 변환
   ↓
Threshold / Segmentation으로 binary mask 생성
   ↓
Morphology로 노이즈 정리
   ↓
connectedComponentsWithStats()
   ↓
stats / centroids 기반 후보 분석
   ↓
면적, 크기, 비율 조건으로 filtering
   ↓
최종 객체 후보 표시 또는 마스크 재구성

이때, 입력 이미지는 반드시 배경과 전경이 구분된 binary mask 형태여야 한다. 일반적으로 배경은 0, 전경은 255 또는 1로 표현한다. (배경은 0, 0이 아닌 값은 전경으로 해석)

binary.dtype == np.uint8
background = 0
foreground = 255

OpenCV에서는 cv.connectedComponents() 또는 cv.connectedComponentsWithStats()를 사용해 CCA를 수행할 수 있다. connectedComponents()는 각 픽셀이 어떤 component에 속하는지를 나타내는 label map을 반환하고, connectedComponentsWithStats()는 여기에 더해 각 component의 위치, 크기, 면적, 중심 좌표까지 함께 반환한다. 이때, num_labels는 라벨의 총 개수이다. 이 라벨의 총 개수에는 배경도 label 하나로 포함된다. 그래서 실제 객체 개수는 보통 실제 객체 개수 = num_labels - 1로 계산할 수 있다.

connectedComponents()
→ 객체 개수와 label map만 필요할 때 사용

connectedComponentsWithStats()
→ 객체 개수, label map뿐만 아니라
   각 객체의 위치, 크기, 면적, 중심 좌표까지 필요할 때 사용
num_labels, labels = cv.connectedComponents(binary, connectivity=8)
num_labels, labels, stats, centroids = cv.connectedComponentsWithStats(binary)
num_labels
→ 전체 라벨 개수
→ 배경 label 0도 포함됨
→ 실제 객체 수는 보통 num_labels - 1

labels
→ 입력 이미지와 같은 크기의 라벨 맵
→ 각 픽셀이 어떤 component에 속하는지 저장

stats
→ 각 component의 bounding box와 area 정보
→ x, y, width, height, area 포함

centroids
→ 각 component의 중심 좌표
→ (cx, cy) 형태
정보의미
객체 개수연결된 component의 개수
label map각 픽셀이 어느 객체에 속하는지 나타낸 라벨 이미지
bounding box객체를 감싸는 사각형의 위치와 크기
area객체를 구성하는 픽셀 수
centroid객체의 중심 좌표
x, ybounding box의 왼쪽 위 좌표
width, heightbounding box의 너비와 높이

connectivity

CCA에서 중요한 개념 중 하나는 connectivity이다. Connectivity는 어떤 픽셀들을 서로 연결된 것으로 볼지 정하는 기준이다. 4-connectivity는 한 픽셀의 상, 하, 좌, 우 방향만 연결된 이웃으로 본다. 따라서 대각선으로만 맞닿아 있는 픽셀들은 서로 다른 객체로 판단될 수 있다. 반면 8-connectivity는 상, 하, 좌, 우뿐만 아니라 대각선 방향까지 연결된 이웃으로 본다. 따라서 대각선으로 맞닿은 픽셀도 같은 객체로 판단된다.

4-connectivity8-connectivity 는 단순한 정의 차이가 아닌, 대각선으로 맞닿아 있는 픽셀 묶음을 서로 다른 객체로 분리할지, 아니면 하나의 객체로 합칠지를 결정하는 기준이 된다.

이를 좌표를 기준으로 정의하면 다음과 같다. 기준 픽셀 P=(x, y)가 있을 때, 다음과 같이 정리할 수 있다. 즉, 8방향 연결성은 4방향 연결성에 대각선 네 방향을 추가한 개념이다. OpenCV의 connectedComponents()connectedComponentsWithStats()는 바로 이 기준에 따라 픽셀들을 같은 component로 묶는다.

N4(P) = {(x-1,y), (x+1,y), (x,y-1), (x,y+1)}
N8(P) = N4(P) ∪ {(x-1,y-1), (x+1,y-1), (x-1,y+1), (x+1,y+1)}

또, connectedComponents()connectedComponentsWithStats()connectivity 인자로 4-way 또는 8-way connectivity를 선택한다. 이때, connectedComponentsWithStats() 는 label 0을 배경으로 포함하므로 실제 전경 객체 수는 일반적으로 num_labels - 1 로 해석한다.

num_labels, labels, stats, centroids = cv.connectedComponentsWithStats(
    binary,
    connectivity=8,   # 또는 4
    ltype=cv.CV_32S
)

실무 상황에서의 적용

정리하면, 4-connectivity는 대각선 접촉을 연결로 보지 않기 때문에 객체를 더 엄격하게 분리한다. 반대로 8-connectivity는 대각선 접촉도 연결로 보기 때문에 더 넓은 범위의 픽셀을 하나의 객체로 묶을 수 있다. 실제 적용에서는 연결성만 단독으로 보지 말고, 전처리 결과와 객체의 형태적 특성을 함께 살펴야 한다. 문서 스캔, OCR, 세포/입자 분석처럼 “살짝 대각선으로 붙은 픽셀을 같은 객체로 볼 것인가?”가 중요한 문제에서는 connectivity 선택이 결과에 직접적인 영향을 줄 수 있다.

profile
얼렁뚱땅 바보 학부생...

0개의 댓글