본문 바로가기
강의 (Lecture)/OpenCV 마스터 with Python (초급)

OpenCV + Python 이진화 (임계처리)

by codingwalks 2024. 10. 22.
728x90
반응형

 

안녕하세요. 코딩산책입니다.

이미지 이진화(Thresholding)는 이미지 처리에서 중요한 기술 중 하나로, 픽셀 값을 흑백 두 가지 값으로 변환하여 불필요한 정보를 제거하고 핵심 부분만 추출하는 데 사용됩니다. 이진화는 노이즈 제거, 객체 검출, 경계 추출 등 다양한 문제 해결에 유용합니다. 이번 글에서는 OpenCV를 활용해 Python에서 다양한 이진화 기법을 사용하는 방법을 살펴보겠습니다. 기본적인 cv2.threshold 함수부터 Otsu 이진화, 적응형 이진화 기법까지, 실제 예제를 통해 알아보겠습니다.

 

1. 기본 이진화 (cv2.threshold)

cv2.threshold는 이미지 이진화의 기본 함수로, 고정된 임계값을 사용해 픽셀 값을 흑백으로 변환합니다. 이 함수는 다양한 파라미터와 이진화 방법을 제공하여 여러 시나리오에 맞춰 적용할 수 있습니다.

2.1. 기본 사용법

retval, dst = cv2.threshold(src, thresh, maxval, type)
  • src: 입력 이미지 (일반적으로 그레이스케일 사용)
  • thresh: 임계값
  • maxval: 임계값을 넘는 픽셀에 적용할 값 (흰색, 보통 255)
  • type: 이진화의 방식 선택

2.2. 다양한 Threshold Type

  • cv2.THRESH_BINARY: 임계값보다 크면 maxval, 작으면 0으로 설정.
  • cv2.THRESH_BINARY_INV: 임계값보다 크면 0, 작으면 maxval로 설정.
  • cv2.THRESH_TRUNC: 임계값을 넘는 값은 임계값으로 설정, 작으면 그대로 유지.
  • cv2.THRESH_TOZERO: 임계값을 넘는 값은 유지, 작으면 0으로 설정.
  • cv2.THRESH_TOZERO_INV: TOZERO와 반대로 작으면 값을 유지, 크면 0으로 설정.

2.3. cv2.Threshold()을 활용한 예제소스

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('resources/lena.bmp', cv2.IMREAD_GRAYSCALE)

ret, th_binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret, th_binary_inv = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret, th_trunc = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
ret, th_tozero = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
ret, th_tozero_inv = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)

titles = ['ORIGINAL', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, th_binary, th_binary_inv, th_trunc, th_tozero, th_tozero_inv]

plt.figure(figsize=(10, 10))
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()

cv2.threshold() 결과 이미지

 

 

2. 적응형 임계값 처리 (cv2.adaptiveThreshold)

2.1. 고정된 임계값의 한계

조명이 균일하지 않거나, 이미지의 밝기가 일정하지 않은 경우 고정된 임계값을 사용한 이진화는 부정확한 결과를 초래할 수 있습니다.

2.2. 적응형 이진화의 필요성

적응형 이진화는 이미지의 각 영역마다 다른 임계값을 계산해 적용하므로, 조명이 불균일한 이미지를 처리할 때 유용합니다.

2.3. cv2.ADAPTIVE_THRESH_MEAN_C

이 방식은 주변 픽셀 값의 평균을 기준으로 임계값을 계산합니다. 이 방식의 원리는 이미지의 각 작은 블록에서 주어진 영역 내의 픽셀 값들의 평균을 계산하고, 그 평균에서 \(C\) 값을 뺀 값을 임계값으로 사용합니다. 이를 통해 각 작은 블록 또는 영역의 픽셀 값에 따라 임계값을 다르게 설정하여, 이미지의 국부적인 조명 변화에도 적응할 수 있게 합니다. 적용 가능한 시나리오로는 조명이 비교적 일정하거나, 복잡한 텍스처가 없는 이미지에서 사용하기 적합하며, 간단한 문서나 텍스트 이미지에서 잘 동작합니다.

\[T(x, y) = \frac{1}{N} \sum_{i,j \in N(x, y)} I(i, j) - C\]

여기서, \(N(x, y)\) 는 주위의 픽셀 블록, \(I(i, j)\) 는 해당 픽셀의 값, \(C\) 는 상수로, 임계값을 세밀하게 조정하는 데 사용됩니다.

2.4. cv2.ADAPTIVE_THRESH_GAUSSIAN_C

이 방식은 주변 픽셀 값에 가우시안 가중치를 적용하여 임계값을 계산합니다. 이 방식의 원리는 주어진 블록 내의 각 픽셀에 가우시안 커널을 적용해 가중 평균을 계산한 후, 그 값을 임계값으로 사용하고, 여기에 C 값을 차감합니다. 즉, 중심에 가까운 픽셀에 더 높은 가중치를 부여하고, 중심에서 멀어질수록 가중치가 줄어드는 방식입니다. 이는 주변 영역의 정보를 가우시안 방식으로 더 세밀하게 반영하므로, 텍스처나 경계가 복잡한 이미지에서 유리합니다. 적용 가능한 시나리오로는 조명이 복잡하게 변화하는 이미지나 텍스처가 복잡한 이미지, 경계가 분명한 물체가 있는 이미지에 적합합니다. 예를 들어, 문서 스캔에서 배경이 일정하지 않은 경우, 이 방법이 더 나은 결과를 제공할 수 있습니다.

\[T(x, y) = \sum_{i,j \in N(x, y)} w(i,j)\cdot I(i, j) - C\]

여기서 \(w(i,j)\) 는 가우시안 가중치, \(I(i, j)\) 는 해당 픽셀의 값입니다.

2.5. cv2.adaptiveThreshold 함수 사용법

dst = cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)

 

  • adaptiveMethod: cv2.ADAPTIVE_THRESH_MEAN_C, cv2.ADAPTIVE_THRESH_GAUSSIAN_C.
  • thresholdType: 기본 이진화의 다양한 Threshold Type 참조.
  • blockSize: 임계값을 계산할 영역 크기 (홀수).
  • C: 평균에서 차감할 상수. 조절을 통해 이진화 결과를 세밀하게 조정.

2.6. cv2.adaptiveThreshold()을 활용한 예제소스

import cv2
from matplotlib import pyplot as plt 

img = cv2.imread('resources/sudoku.jpg', cv2.IMREAD_GRAYSCALE)

th_adap_mean = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 45, 5)
th_adap_gaussian = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 45, 5)

titles = ['Original', 'Mean', 'Gaussian']
images = [img, th_adap_mean, th_adap_gaussian]

plt.figure(figsize=(10, 4))
for i in range(len(images)):
	plt.subplot(2,2,i+1), plt.imshow(images[i],'gray')
	plt.title(titles[i])
	plt.xticks([]), plt.yticks([])

plt.tight_layout()
plt.show()

cv2.adaptiveThreshold() 결과 이미지

 

3. 오츠의 이진화 (Otsu's Threshold)

Otsu의 이진화는 이미지 이진화 기법 중 하나로, 임계값을 자동으로 결정하는 방법입니다. 수동으로 임계값을 설정해야 하는 일반적인 이진화와 달리, 이미지의 히스토그램을 분석하여 최적의 임계값을 찾습니다. 이 방법은 이미지의 전경과 배경이 뚜렷하게 구분되는 경우에 특히 효과적입니다. Otsu의 이진화는 전역적 이진화 방법 중 하나로, 이미지의 히스토그램을 두 개의 클래스(전경과 배경)로 나눈 후, 그 클래스 간의 분산(Variance)을 최소화하는 임계값을 자동으로 계산합니다.

  • 전경(객체): 우리가 관심을 가지는 이미지의 부분.
  • 배경: 전경을 제외한 나머지 부분.

Otsu 방법은 픽셀 값을 두 그룹으로 나누었을 때, 그 그룹들 사이의 분산(클래스 간 분산, Between-class variance)을 최대화하는 임계값을 찾는 원리로 작동합니다. 이렇게 하면 전경과 배경을 가장 잘 구분하는 임계값을 자동으로 결정할 수 있습니다.

3.1. Otsu 이진화의 한계

  • 조명이 불균일한 이미지에서의 한계: Otsu의 이진화는 이미지 전체에 대해 단일 임계값을 적용하므로, 조명이 고르지 않거나 배경과 전경이 명확하지 않은 이미지에서는 성능이 떨어질 수 있습니다. 이 경우 적응형 임계값 처리(Adaptive Thresholding)지역적 이진화(Local Thresholding) 기법을 사용해야 합니다.
  • 노이즈에 취약: 이미지에 많은 노이즈가 포함되어 있으면, 히스토그램이 흐트러져서 Otsu 알고리즘이 적절한 임계값을 찾기 어려울 수 있습니다. 이 경우 가우시안 블러링(Gaussian Blurring)을 적용한 후 Otsu 이진화를 사용하는 것이 좋습니다.

3.2. Otsu 이진화의 과정

Otsu 이진화는 히스토그램을 분석해 클래스 간의 분산이 최대가 되는 임계값을 선택합니다. 여기서, 클래스 간 분산이 최대가 될 때 두 클래스가 가장 잘 구분된다고 가정합니다.

3.2.1. 이미지 히스토그램 생성

먼저, 이미지를 그레이스케일로 변환한 후 각 픽셀 값의 분포를 나타내는 히스토그램을 생성합니다. 히스토그램은 각 그레이스케일 값의 빈도수(픽셀 수)를 나타내는 그래프입니다.

3.2.2. 클래스 내 분산과 클래스 간 분산 정의

전체 픽셀 값을 두 그룹으로 나눈 후, 각 그룹의 평균값과 분산을 계산합니다. Otsu 방법에서 중요한 개념은 클래스 내 분산클래스 간 분산입니다.

  • 클래스 내 분산: 각 그룹의 픽셀 값들이 얼마나 퍼져 있는지를 나타냅니다.
  • 클래스 간 분산: 두 그룹 간의 평균 차이를 나타내며, 이 값이 최대가 될 때 두 그룹이 가장 잘 구분됩니다.

3.2.3. 임계값 최적화

이미지의 모든 가능한 임계값 t에 대해 아래 공식을 사용하여 클래스 간 분산을 계산합니다. 최적의 임계값은 클래스 간 분산이 최대가 되는 임계값입니다.

\[\sigma_b^2(t) = w_0(t) \cdot w_1(t) \cdot [\mu_0(t) - \mu_1(t)]^2\]

여기서,

  • \(w_0(t), w_1(t)\): 각각의 클래스(배경과 전경)의 확률 (즉, 픽셀 비율).
  • \(\mu_0(t), \mu_1(t)\): 각각의 클래스의 평균값.

이 식에서 클래스 간 분산이 최대가 되는 t 값을 최적의 임계값으로 선택합니다.

3.3. cv2.threshold()에서 Otsu 적용

import cv2

img = cv2.imread('resources/noisy_leaf.jpg', cv2.IMREAD_GRAYSCALE)

blur = cv2.GaussianBlur(img, (9, 9), 0)

ret, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

cv2.imshow('Gaussian + Otsu Binary', thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

otsu's threshold 결과 이미지

 

4. 결론

이번 포스팅에서 다룬 다양한 이진화 기법들은 이미지 처리의 중요한 기초 기술입니다. OpenCV를 통해 Python에서 쉽게 다양한 이진화 기법을 사용할 수 있으며, 각 기법의 특성과 장점을 이해하면 이미지 처리의 효율을 극대화할 수 있습니다. 이진화는 이미지를 간단하게 전경과 배경으로 분리해 주는 강력한 도구이며, 객체 검출, 노이즈 제거, 문서 스캔 등 다양한 분야에서 활용됩니다. cv2.threshold()를 통해 고정 임계값을 사용하는 기본적인 이진화 방법 확인해 보았습니다. 또한, 적응형 이진화를 통해 조명이 불균일하거나 복잡한 이미지에서도 효과적인 결과를 얻을 수 있었습니다. 특히 cv2.adaptiveThreshold()에서 MEAN_C와 GAUSSIAN_C는 각각의 상황에 맞는 선택지를 제공합니다. 마지막으로, Otsu 이진화는 이미지 히스토그램 분석을 통해 임계값을 자동으로 계산하는 강력한 기법이며, 전경과 배경이 명확한 이미지에서 탁월한 성능을 발휘합니다. 하지만, 조명이 불균일하거나 노이즈가 많은 경우 적응형 이진화나 가우시안 블러와 같은 방법과 결합해 사용하는 것이 더 나은 결과를 제공합니다. 이진화는 컴퓨터 비전 및 이미지 처리의 기본적이면서도 중요한 단계로, 다양한 응용 분야에서 활용될 수 있습니다. 앞으로 모폴로지 연산, 객체 검출, 세그멘테이션 등의 고급 이미지 처리 기법을 학습하면서, 이진화 기법을 보다 깊이 있게 활용할 수 있을 것입니다.

 

해당 포스트가 유용하셨다면 하단의 좋아요와 구독하기 부탁드립니다. ^^

Buy me a coffee

 

[Codingwalks]에게 송금하기 - AQR

[Codingwalks]에게 송금하기 - AQR

aq.gy

★ 모든 내용은 아래의 링크를 참조하였습니다. ★

 

OpenCV: OpenCV-Python Tutorials

Core Operations In this section you will learn basic operations on image like pixel editing, geometric transformations, code optimization, some mathematical tools etc.

docs.opencv.org

728x90
반응형