안녕하세요? 종강 후 실컷 놀러다니다 다시 공부를 시작하겠습니다.
오늘은 컴퓨터비전 분야에서 image를 각 영역(혹은 클래스)로 분할하는 Image Segmentation의 정의와 그 방법에 대해 알아보고, 실제 python 코드를 이용해 입력 image를 다양한 방법으로 분류해보겠습니다.
[1] Image segmentation이란?
이미지의 각 픽셀을 의미 있는 부분으로, 그렇게 서로 다른 영역으로 나누는 것을 말합니다. 이 과정은 각 픽셀을 하나 이상의 영역으로 그룹화하여 이미지 상에서 객체나 구조를 인식하고 분석하거나 의료 진단, 자율 주행 등의 영역에서 폭넓게 쓰입니다. Computer vision에서 매우 중요한 요소라고 할 수 있죠. 암암.
Image segmentation에는 여러 기법이 있습니다.
- 1. segmentation based Threshold
- 2. segmentation based Edge
- 3. segmentation based Region
- 4. segmentation based Deep learing
위의 기법 중 자신의 목적에 부합하는 segmentation 기법을 선택하시면 됩니다.
딥러닝은 딥하기에 생략할 예정이고, 이 글의 후반부에서 다루게 될 region-based segmentation에서는 비슷한 특성을 가진 픽셀을 모아서 분할하는데 이 때 나누는 기준은 similar property 입니다. 쉽게 말해서 비슷한 놈들끼리 묶어서 하나의 영역으로 치겠다 정도죠.
이 때 영역 분리 기준인 property의 유사도를 후하게 쳐주면 이미지가 undersegmented 될 가능성이 있고, 반대로 기준을 너무 빡빡하게 하면 이미지가 oversegmented될 수 있습니다.
위의 예시를 보면 Input image를 각 영역으로 분할하는 과정에서 일어날 수 있는 경우들을 확인하실 수 있습니다. 뭐든 적당한 게 좋다지만, 그 기준을 찾는 건 어려울지도 모르겠습니다.
[2] Image Segmentation
semantic / instance / panoptic 등의 여러 타입이 있으며, 방법 또한 여러가지가 있습니다. 이번 장은 영역 분할에 대한 설명과 간단한 예제를 다루기에 딥러닝을 기반으로 이루어지는 FCNs, U-Net, Mask R-CNN 등은 추후에 소개하도록하고 low-level에서 이루어지는 기본적인 방법들에 대해 알아보겠습니다.
(1) 임계값 기반 : Thresholding
특정 임계값을 기준으로 각 픽셀의 값이 임계값보다 크면 1, 아니면 0으로 이미지를 이진화하는 것입니다. 사실 이 자체로 하나의 의미있는 영역 분할 방법이라고 하기엔 어렵지만, 만약 각 픽셀의 값이 특정 값을 넘기는지의 여부만 중요할 때 요긴하게 쓸 수 있습니다. 예시로 0~255 사이의 값을 갖는 픽셀들을 127을 기준으로 하여 분류하는 코드를 살펴본 후, 실제로 분류해보겠습니다.
import cv2
import numpy as np
from matplotlib import pyplot as plt
# 이미지 읽기
image = cv2.imread('Write your image path', 0)
# Thresholding 적용
_, thresholded = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
# 결과 표시
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(image, cmap='gray')
plt.subplot(1, 2, 2)
plt.title('Thresholded Image')
plt.imshow(thresholded, cmap='gray')
plt.show()
(2) 윤곽선 기반 : Edge Detection
다음 방법은 이미지의 경계를 감지하여 객체를 분할하는 방법입니다. edge에 대한 설명은 이전 게시글을 참조해주시면 됩니다. 바로 코드와 예시 이미지 참고하겠습니다.
import cv2
import numpy as np
from matplotlib import pyplot as plt
# 이미지 읽기
image = cv2.imread('Write your image path', 0)
# Canny Edge Detection 적용
edges = cv2.Canny(image, 100, 200)
# 결과 표시
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(image, cmap='gray')
plt.subplot(1, 2, 2)
plt.title('Edge Detection')
plt.imshow(edges, cmap='gray')
plt.show()
(3.1) 지역 기반 : Region Growing
3번은 단순한 이미지 분할이 아니라, 비슷한 특성인 similar property를 이용하여 이미지를 서로 다른 영역으로 분할하는 region-based segmentation에 해당하는 방법입니다.
처음으로 알아볼 3.1의 Region growing 방법은 이미지에 몇 개의 seed pixel을 초기값으로 두고, 해당 시드에서 시작하여 시드와 유사한 속성을 가진 이웃 픽셀들을 영역에 추가하는 방법입니다.
이 때 초기 시드 포인트 선택이 중요하며, 새로운 픽셀이 기준 픽셀인 seed와 유사한지를 검사하여 영역에 더할지 말지를 결정하는 statistical test를 거치게 됩니다. 기본적으로 oversegmentation 상태에서 수행하게 됩니다. 코드와 함께 예시 살펴보겠습니다.
import cv2
import numpy as np
import matplotlib.pyplot as plt
def region_growing(image, seed, threshold=10):
height, width = image.shape
segmented = np.zeros((height, width), np.uint8)
stack = [seed]
while stack:
x, y = stack.pop()
segmented[x, y] = 255
for dx in range(-1, 2):
for dy in range(-1, 2):
nx, ny = x + dx, y + dy
if 0 <= nx < height and 0 <= ny < width and segmented[nx, ny] == 0:
if abs(int(image[x, y]) - int(image[nx, ny])) < threshold:
stack.append((nx, ny))
segmented[nx, ny] = 255
return segmented
# 예제 이미지 로드
image = cv2.imread('Write your image path', 0) # 흑백 이미지 로드
seed_point = (3, 3) # 시드 포인트 설정
# Region Growing 수행
segmented_image = region_growing(image, seed_point)
# 결과 시각화
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(image, cmap='gray')
plt.subplot(1, 2, 2)
plt.title('Segmented Image (Region Growing)')
plt.imshow(segmented_image, cmap='gray')
plt.show()
(3.2) 지역 기반 : Clustering
마찬가지로 region-based segmentation 기법 중 하나인 Clustering에 대해 배워보겠습니다. 앞서 살펴본 Region growing 방식은 초기 시드 픽셀에서 시작하여 영역을 확장하는, 이미지 상에서 픽셀의 배치 순서까지 고려한 spacial information을 포함하는 픽셀 기반의 접근법이었다면 Clustering 방식은 그냥 픽셀 값 하나만 생각하면 됩니다.
Clustering 방식에서는 각 픽셀 값들만 보며, 값이 비슷한 픽셀들이 같은 영역에 속합니다. 3차원 RGB Image를 이용한 clustering 예시 살펴보겠습니다.
위의 그림에서 Image의 각 픽셀은 (r,g,b)의 시퀀스로 표현할 수 있을 것이고, 이를 3차원 좌표공간에 x로 나타내어 보겠습니다. 그리고, 각 픽셀의 거리가 가깝다는 것은 서로의 픽셀 값이 비슷하다는 것이므로 같은 cluster 그룹으로 묶어줍니다. 이러한 방식을 사용하다보니, Clustering 방식에서는 픽셀의 배치 등의 spacial information을 고려하지 않습니다.
주로 사용되는 클러스터링 기법은 K-means clustering 인데, K개의 cluster group을 상정한 후 위의 동작 과정을 거친다고 보시면 됩니다. 자세한 설명은 살짝 복잡하고 저는 집을 가고 싶으니 다른 똑똑한 분의 블로그를 참고해주시면 좋을 것 같고, 코드와 함께 예시 살펴보겠습니다.
import cv2
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
def kmeans_segmentation(image, k=2):
pixel_values = image.reshape((-1, 3))
pixel_values = np.float32(pixel_values)
kmeans = KMeans(n_clusters=k, random_state=0).fit(pixel_values)
labels = kmeans.labels_
centers = kmeans.cluster_centers_
segmented_image = centers[labels.flatten()]
segmented_image = segmented_image.reshape(image.shape)
segmented_image = np.uint8(segmented_image)
return segmented_image
# 예제 이미지 로드
image = cv2.imread('Write your image path')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# K-means Clustering 수행
k = 3 # 클러스터 수 설정
segmented_image = kmeans_segmentation(image, k)
# 결과 시각화
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(image)
plt.subplot(1, 2, 2)
plt.title('Segmented Image (K-means)')
plt.imshow(segmented_image)
plt.show()
마찬가지로, K-means clustering 방법에서도 초기 k값 설정이 중요하다고 볼 수 있습니다.
저는 예시 코드에서 초기 k값을 3으로 설정하였기에 결과 이미지에 3개의 그룹으로 Image가 분할된 것을 볼 수 있습니다. 3개 그룹의 대표색은 각 그룹에 속하는 픽셀 값들의 평균이며, 자세한 설명은 기회가 된다면 다루어보도록 하겠습니다. 감사합니다.
'컴퓨터비전 (CV)' 카테고리의 다른 글
[컴퓨터비전] (5) Linear Regression, 선형회귀분석 (5) | 2024.07.24 |
---|---|
[컴퓨터비전] (3) Shape에 대하여 (0) | 2024.06.14 |
[컴퓨터비전] (2) Texture 에 대하여 (2) | 2024.06.13 |
[컴퓨터비전] (1) Color, Edge에 대하여 (0) | 2024.03.31 |