안녕하세요. 코딩산책입니다.
이미지 처리는 다양한 분야에서 중요한 역할을 하고 있으며, 그중에서도 모폴로지 연산과 워터쉐드 알고리즘은 이미지의 구조적 특성을 분석하고 객체를 분리하는 데 매우 유용한 기법입니다. 모폴로지 연산은 객체의 형상을 조정하고, 객체 간 경계를 구분하며, 이미지 노이즈를 제거하는 데 사용됩니다. 이 글에서는 침식(Erosion), 팽창(Dilation), 열기(Opening), 닫기(Closing), 그레이디언트(Gradient)와 같은 모폴로지 연산과 워터쉐드 알고리즘의 이론적 배경과 함께 Python과 OpenCV를 이용한 실제 코드 예제를 다룹니다. 이 연산들은 특히 동전 분류나 지문 융선 검출과 같은 응용 사례에서 큰 효과를 발휘합니다.
1. 모폴로지(Morphology) 란?
모폴로지(Morphology)는 수학적 모양 이론을 기반으로 이미지를 처리하는 기법입니다. 이 기법은 주로 바이너리 이미지(흑백 이미지)에서 객체의 모양이나 구조를 분석하고 처리하는 데 사용됩니다. 모폴로지 연산은 이미지 내에서 객체의 크기, 모양, 경계를 변화시킴으로써 객체를 명확히 구분하거나, 이미지 내의 노이즈를 제거하는 데 중요한 역할을 합니다.
모폴로지 연산은 이미지의 픽셀 값을 바탕으로, 이웃하는 픽셀들과의 관계를 고려하여 이미지의 모양을 변형합니다. 이러한 연산은 물리적으로는 픽셀을 "확장"하거나 "축소"시키는 것처럼 보일 수 있습니다. 예를 들어, 객체의 경계를 다듬거나, 불필요한 노이즈를 제거하는 데 유용합니다.
모폴로지 연산의 주요 목적:
- 노이즈 제거: 작은 노이즈를 제거하여 이미지의 중요한 구조를 보존함.
- 객체 분리: 연결된 객체를 분리하여 구분 가능하게 함.
- 구멍 메우기: 객체 내부의 작은 구멍을 채움.
- 형태 분석: 객체의 모양이나 경계를 추출함.
모폴로지 연산의 주요 개념은 커널(structuring element)을 통해 객체의 모양을 변화시키는 것입니다. 커널은 일정한 크기와 모양을 가진 작은 행렬로, 이 행렬이 이미지 위를 이동하면서 특정한 연산을 수행합니다.
2. 모폴로지 연산의 기본 요소
모폴로지 연산의 기본 요소는 커널(structuring element)과 연산이 적용되는 이미지입니다. 이 두 요소는 모폴로지 연산의 결과에 결정적인 영향을 미치며, 어떻게 연산이 이루 어질지를 결정합니다.
2.1. 커널(structuring element)
커널은 모폴로지 연산에서 핵심적인 역할을 하는 작은 행렬(matrix)입니다. 커널은 보통 정방형(예: 3x3, 5x5) 혹은 다른 규칙적인 형태를 가지고 있습니다. 커널이 이미지 위를 움직이면서 특정 연산을 수행하며, 그 연산의 결과는 커널의 모양과 크기에 의해 크게 좌우됩니다.
- 커널의 모양: 커널은 사각형, 원형, 십자형 등 다양한 형태로 정의될 수 있습니다. 이러한 커널의 형태는 객체가 어떻게 변형되는지에 따라 선택됩니다.
- 커널의 크기: 커널의 크기는 연산의 강도를 결정합니다. 예를 들어, 더 큰 커널을 사용하면 침식(erosion) 연산에서 객체가 더 많이 축소되고, 팽창(dilation) 연산에서는 객체가 더 많이 확장됩니다.
커널은 각 픽셀 주변의 이웃 픽셀들을 정의하는 역할을 하며, 이미지에서 관심 있는 부분의 패턴을 찾아내거나, 경계를 강화할 수 있습니다. 커널은 이미지 내에서 한 픽셀씩 이동하며 각 위치에서 연산을 수행합니다.
2.2. 모폴로지 연산의 기초 수식
모폴로지 연산은 기본적으로 집합 이론에 기초하고 있습니다. 이미지에서 E는 이진화된 이미지로 표현되고, B는 커널을 나타냅니다. 두 연산의 기본 개념을 수식으로 나타내면 다음과 같습니다.
- 침식(Erosion): 침식 연산은 객체를 축소시키는 역할을 합니다. 수식적으로, 침식은 다음과 같이 정의됩니다. \(E \ominus B = \{ z \in E \mid (B)_z \subseteq E \}\) 여기서 \(\ominus\) 는 침식 연산을 의미하며, B 는 커널입니다. 즉, 커널 B 가 이미지 E 내에서 완전히 포함되는 모든 위치 z 를 찾는 연산입니다.
- 팽창(Dilation): 팽창 연산은 객체를 확장시키는 역할을 합니다. 팽창 연산은 다음과 같이 정의됩니다. \(E \oplus B = \{ z \in E \mid (B)_z \cap E \neq \emptyset \}\) 여기서 \(\oplus\) 는 팽창 연산을 나타내며, B 는 커널입니다. 팽창은 커널의 일부가 이미지와 겹치는 모든 위치를 찾는 과정입니다.
2.3. 연산의 적용 방식
커널이 이미지 위를 움직이면서 해당 커널이 차지하는 영역 내에서 특정 조건을 만족하는지에 따라, 해당 픽셀 값이 변경됩니다. 예를 들어, 침식 연산에서는 커널이 객체 내부에 완전히 포함되는 경우에만 그 픽셀이 유지되고, 그렇지 않으면 제거됩니다. 반대로 팽창 연산에서는 커널이 객체와 겹치는 모든 경우에 픽셀이 확장됩니다.
2.4. 이진화된 이미지와 그레이스케일 이미지
모폴로지 연산은 주로 이진화된 이미지에서 사용되지만, 그레이스케일 이미지에도 적용될 수 있습니다. 이진화된 이미지에서는 픽셀 값이 0(검은색)과 255(흰색)로 구성되지만, 그레이스케일 이미지에서는 픽셀 값이 0에서 255까지의 연속적인 값을 가집니다. 그레이스케일 이미지에서의 모폴로지 연산은 밝기 차이를 기반으로 수행되며, 경계나 객체의 특징을 강조하는 데 사용됩니다.
3. 모폴로지 알고리즘
3.1. 침식(Erosion)
침식(Erosion) 연산은 이미지에서 객체의 경계를 축소시키는 연산입니다. 커널(structuring element)이 객체의 외곽을 따라 이동하면서 커널이 완전히 객체 내부에 포함되는 경우에만 그 영역의 픽셀이 남고, 그렇지 않으면 제거됩니다. 이로 인해 객체는 축소되고, 작은 노이즈가 제거됩니다.
- 침식 연산은 객체를 깎아내는 것처럼 동작하여 객체의 경계선이 점차 줄어듭니다.
- 주로 객체의 외곽에 붙어 있는 노이즈를 제거하는 데 사용됩니다.
- 커널의 크기가 커질수록 객체의 크기 감소 폭이 커집니다.
3.2. 팽창(Dilation)
팽창(Dilation) 연산은 침식의 반대 연산으로, 객체의 경계를 확장합니다. 커널이 이미지 위를 이동하면서 커널 영역 내에 하나라도 객체 픽셀이 포함되어 있으면 해당 영역의 픽셀 값이 모두 1(흰색)로 설정됩니다. 이로 인해 객체의 크기가 커지고, 작은 구멍이 메워집니다.
- 팽창 연산은 객체를 "부풀리는" 것처럼 동작하여 객체의 크기를 증가시킵니다.
- 작은 구멍이나 끊어진 부분을 메우는 데 유용합니다.
- 커널의 크기가 커질수록 객체가 확장되는 폭도 커집니다.
3.3. 열기(Opening)
열기(Opening) 연산은 침식 후 팽창을 수행하는 연산입니다. 이 과정에서 객체의 외곽에 붙어 있는 작은 노이즈가 제거되지만, 객체의 크기나 형태는 크게 변화하지 않습니다. 따라서 작은 노이즈 제거에 효과적입니다.
- 열기는 침식(Erosion)을 먼저 적용한 후, 팽창(Dilation)을 적용하는 과정입니다.
- 침식으로 인해 작은 노이즈가 제거되고, 팽창으로 원래 형태를 복원하는 역할을 합니다.
- 주로 작은 물체나 노이즈를 제거하는 데 사용됩니다.
3.4. 닫기(Closing)
닫기(Closing) 연산은 팽창 후 침식을 수행하는 연산입니다. 객체의 경계선을 유지하면서 내부의 작은 구멍이나 틈을 메워줍니다. 이는 객체가 연결된 부분을 끊김 없이 유지하고, 내부의 작은 결함을 채우는 데 유용합니다.
- 닫기는 팽창(Dilation)을 먼저 적용한 후, 침식(Erosion)을 적용합니다.
- 팽창으로 객체가 확장되고, 침식으로 원래 형태가 복원되지만 구멍이나 틈은 채워집니다.
- 주로 객체 내부의 작은 구멍을 메우거나, 객체 간 연결을 유지하는 데 사용됩니다.
3.5. 그레디언트(Gradient)
모폴로지 그레이디언트(Morphological Gradient)는 팽창과 침식의 차이를 계산하는 연산입니다. 객체의 경계를 강조하는 데 매우 유용하며, 엣지 검출을 수행할 때 주로 사용됩니다. 팽창과 침식 간의 차이를 계산하여 경계를 강조합니다.
- 그레이디언트는 팽창된 이미지와 침식된 이미지의 차이를 나타냅니다.
- 이를 통해 객체의 경계선 부분이 강조되어 나타나게 됩니다.
- 경계선이나 객체 외곽을 추출하는 데 유용합니다.
3.6. 소스코드
import cv2
import numpy as np
image = cv2.imread('resources/lena.bmp', cv2.IMREAD_GRAYSCALE)
# Kernel definition (size 5x5)
kernel = np.ones((5, 5), np.uint8)
# Apply erosion operation
erosion = cv2.erode(image, kernel, iterations=1)
# Applying dilation operation
dilation = cv2.dilate(image, kernel, iterations=1)
# Apply opening operation
opening = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
# Apply closing operation
closing = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
# Apply gradient operation
gradient = cv2.morphologyEx(image, cv2.MORPH_GRADIENT, kernel)
titles = ['Original', 'Erosion', 'Dilation', 'Opening', 'Closing', 'Gradient']
images = [thresh, erosion, dilation, opening, closing, gradient]
plt.figure(figsize=(10, 7))
for i in range(len(images)):
plt.subplot(2, 3, i+1), plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()
4. 워터쉐드(Watershed) 알고리즘
워터쉐드(Watershed) 알고리즘은 이미지 세분화 기법 중 하나로, 다양한 객체나 구조를 구분하는 데 사용됩니다. 이 알고리즘은 지형적 해석에서 영감을 받았으며, 이미지에서 서로 다른 영역(예: 객체와 배경)을 분리하는 데 효과적입니다.
워터쉐드 개념:
- 지형적 해석: 워터쉐드 알고리즘은 이미지를 지형의 높이 지도처럼 해석합니다. 즉, 이미지에서 밝은 영역은 산봉우리로, 어두운 영역은 계곡으로 간주됩니다. 워터쉐드는 물이 채워질 때, 서로 다른 영역이 어떻게 분리되는지를 분석하여 경계를 형성합니다.
- 마커(Markers): 워터쉐드 알고리즘에서는 먼저 마커를 설정하는데, 이 마커는 객체와 배경을 나타냅니다. 마커는 객체 내부와 배경을 명확히 구분할 수 있는 곳에 설정되며, 알고리즘이 이 마커를 바탕으로 객체의 경계를 찾아냅니다.
알고리즘 작동 원리:
- 마커 설정: 객체와 배경을 구분하기 위한 마커를 설정합니다. 마커는 워터쉐드 알고리즘이 시작되는 지점으로, 명확히 구분된 객체 내부와 배경을 표시합니다.
- 워터쉐드 연산: 마커가 있는 지점부터 물을 채워나가며, 서로 다른 마커가 만나는 지점에서 경계가 형성됩니다.
- 결과: 경계가 완성되면, 이미지가 여러 개의 영역으로 분리됩니다. 이때 경계는 객체와 객체, 또는 객체와 배경을 구분합니다.
워터쉐드 알고리즘은 지문 인식, 의료 영상 분석, 동전 분류와 같은 이미지 세분화 문제에서 매우 유용합니다.
4.1. 워터쉐드 알고리즘을 이용한 동전 분류 예제
이 코드는 여러 동전이 섞여 있는 이미지를 분석하여 각각의 동전을 분리하는 데 사용됩니다. 워터쉐드 알고리즘을 활용하여 동전의 경계를 구분합니다. 이 코드는 동전 이미지를 불러와 워터쉐드 알고리즘을 적용하여 동전의 경계를 찾아냅니다. 이 과정에서 이진화, 팽창, 열기 등 다양한 모폴로지 연산을 사용합니다.
import cv2
import numpy as np
from matplotlib import pyplot as plt
image = cv2.imread('resources/coins.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Binarization (using Otsu's thresholding)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# Noise removal (open operation)
kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
# Expanding the object area (dilation operation)
sure_bg = cv2.dilate(opening, kernel, iterations=3)
# Finding the exact area of an object using distance transform
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
_, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)
# Finding boundary area by difference between clear object area and background area
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)
# Create marker
_, markers = cv2.connectedComponents(sure_fg)
# Add 1 to the marker to set the background to 1
markers = markers + 1
# Set the boundary area as a marker
markers[unknown == 255] = 0
image_color = cv2.imread('resources/coins.jpg')
# apply watershed
markers = cv2.watershed(image_color, markers)
image_color[markers == -1] = [255, 0, 0]
titles = ['Gray', 'Binary', 'Sure BG', 'Distance', 'Sure FG', 'Unknown', 'Markers', 'Result']
images = [gray, thresh, sure_bg, dist_transform, sure_fg, unknown, markers, image]
plt.figure(figsize=(10, 5))
for i in range(len(images)):
plt.subplot(2, 4, i+1), plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()
5. 모폴로지 연산의 실제 응용 사례
- 침식(Erosion):
- 텍스트 추출 전처리: 이미지에서 텍스트 추출 시, 작은 노이즈를 제거하고 객체의 윤곽을 간소화하는 데 유용합니다. 예를 들어, 문서 스캔 이미지에서 배경의 작은 점들을 제거할 수 있습니다.
- 팽창(Dilation):
- 차선 인식: 도로 영상에서 차선을 강조하는 데 사용됩니다. 팽창 연산은 차선의 두께를 증가시켜 검출을 용이하게 합니다.
- 열기(Opening):
- 의료 영상 분석: MRI 이미지에서 작은 병변이나 노이즈를 제거하는 데 사용됩니다. 특정 부분을 분석할 때 불필요한 잡음을 제거해 객체만을 추출합니다.
- 닫기(Closing):
- 문자 인식(OCR): 닫기 연산은 이미지 내 작은 구멍이나 단절된 부분을 메워서 문자 인식을 더욱 정확하게 수행할 수 있습니다. 이미지 내의 끊어진 글씨를 이어주는 역할을 합니다.
- 그레이디언트(Gradient):
- 엣지 검출: 객체의 경계를 추출하는 데 매우 유용합니다. 예를 들어, 건물 외곽선 추출이나 자동차 외곽선 검출에 사용될 수 있습니다.
- 워터쉐드(Watershed):
- 지문 분석: 지문 융선의 경계를 감지하여 지문 인식에 사용됩니다.
- 동전 분류: 동전 더미에서 각각의 동전을 분리하여 분류하는 데 사용됩니다.
- 의료 영상 세분화: CT 스캔이나 MRI 이미지를 분석할 때, 각각의 기관이나 조직을 구분하는 데 워터쉐드 알고리즘을 활용할 수 있습니다. 특히 뇌 영상에서 종양과 정상 조직을 구분하는 데 효과적입니다.
- 산업 이미지 처리: 제조업에서 결함을 검사하는 과정에서, 객체와 결함을 구분하는 데 워터쉐드가 사용될 수 있습니다. 예를 들어, 금속 표면의 작은 결함을 감지하는 데 유용합니다.
- 객체 검출 및 추적: 감시 시스템에서 여러 객체가 겹치는 경우, 워터쉐드 알고리즘을 사용하여 서로 다른 객체를 구분할 수 있습니다. 예를 들어, 군중이나 차량이 밀집된 상황에서 객체 간 경계를 구분할 수 있습니다.
6. 결론
모폴로지 연산과 워터쉐드 알고리즘은 이미지 분석 및 처리에서 매우 중요한 역할을 합니다. 침식, 팽창, 열기, 닫기, 그레이디언트와 같은 기본 모폴로지 연산은 객체의 윤곽을 조정하고, 이미지에서 노이즈를 제거하는 데 유용하며, 워터쉐드 알고리즘은 복잡한 이미지에서 객체를 효과적으로 분리할 수 있는 강력한 기법입니다.
이 글에서는 모폴로지 연산과 워터쉐드 알고리즘의 이론적 배경을 상세히 설명하고, 동전 분류 및 지문 융선 감지 등 실제 응용 사례에서 이를 어떻게 사용할 수 있는지 코드 예제를 통해 알아보았습니다. 이러한 기술은 의료, 산업, 보안, 객체 검출 및 추적 등 다양한 분야에서 활용되며, 특히 이미지 처리 문제를 해결하는 데 큰 기여를 합니다. 이를 통해 우리는 이미지 처리와 분석의 효율성을 높이고, 더 나은 결과를 얻을 수 있습니다.
모폴로지 연산과 워터쉐드 알고리즘은 컴퓨터 비전에서 매우 강력한 도구이며, 앞으로도 다양한 분야에서 더욱 널리 활용될 것입니다.
해당 포스트가 유용하셨다면 하단의 좋아요와 구독하기 부탁드립니다. ^^
★ 모든 내용은 아래의 링크를 참조하였습니다. ★
https://opencv-python.readthedocs.io/en/latest/doc/27.imageWaterShed/imageWaterShed.html
'강의 (Lecture) > OpenCV 마스터 with Python (초급)' 카테고리의 다른 글
OpenCV + Python 외곽선 검출과 레이블링(labeling) - 2 (2) | 2024.10.30 |
---|---|
OpenCV + Python 외곽선 검출과 레이블링(labeling) - 1 (0) | 2024.10.30 |
OpenCV + Python 이진화 (임계처리) (5) | 2024.10.22 |
OpenCV + Python 엣지(Edge) 검출과 허프(Hough) 변환 (2) | 2024.10.21 |
OpenCV + Python 필터와 컨볼루션 (0) | 2024.10.14 |