이미지를 흑백으로만 표현한 것을 이진화 이미지(binary image)라고 한다. 이미지에서 원하는 부분을 더욱 정확하게 판단하기 위해서 이를 활용하며, thresholding이라는 방법을 통해서 이진화 이미지를 만들 수 있다. Thresholding이란 입력된 자료군을 임계값과 비교하는 데이터 변환 기법으로, 합격/불합격 여부처럼 새롭게 입력된 자료군이 임계값보다 큰지 작은지에 따라 두 부류로 나누는 것을 의미한다.

 

1. 전역 thresholding

Binary 이미지를 만들기 위해서는 Color 이미지를 Gray scale로 변환해서 각 pixel이 임계값을 넘으면 255(흰색), 그렇지 않으면 0(검은색)이 되도록 한다. 이러한 기능은 cv2.threshold()를 통해 구현한다.

 

import cv2
import numpy as np

img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

# NumPy로 binary image 생성
thres = np.zeros_like(img)
thres[img > 127] = 255

# cv2.threshold()로 binary image 생성
ret, result = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) # _, result로도 가능

cv2.imshow('Original', img)
cv2.imshow('Result', thres)
# cv2.imshow('Result', result) ## cv2.imshow('Result', thres)와 동일하게 출력
cv2.waitKey()
cv2.destroyAllWindows()

gray 변환(좌) / cv2.threshold() 적용(우)

 

cv2.threshold()와 NumPy 모두 동일한 binary 이미지 결과를 얻을 수 있으며, 위는 경계값을 127로 설정했을 때 출력된 결과다. cv2.threshold()는 이미지, 경계값, 경계값 기준을 넘을 때 pixel에 적용할 값, threshold 적용 방법이 인자로 구성되며 경계값과 thresholding된 이미지가 반환된다. 위 코드에서 사용된 변수 ret, result에서 ret은 threshold함수에 전달된 인자와 같기 때문에 굳이 변수를 만들고 싶지 않다면 _, result로 작성해도 무관하다.

 

thresholding을 적용하는 방법의 종류는 다음과 같다. 경계값 기준을 만족할 때 적용할 값을 100으로 가정한다.

cv2.THRESH_BINARY pixel이 경계를 넘으면 100을 지정하고, 그렇지 못하면 0 지정
cv2.THRESH_BINARY_INV cv2.THRESH_BINARY와 반대로 경계를 넘지 못할 때 100 지정
cv2.THRESH_TOZERO pixel이 경계를 넘으면 기존 값을 유지하고, 그렇지 않으면 0 지정
cv2.THRESH_TOZERO_INV cv2.THRESH_TOZERO와 반대로 경계를 넘지 못할 때 기존 값을 유지
cv2.THRESH_TRUNC pixel이 경계를 넘으면 100 지정, 그렇지 않으면 기존 값 유지

 

2. 오츠의 이진화 알고리즘

경계값을 정하는 것에 따라 binary image가 다르게 출력되기 때문에 경계값을 설정하는 것이 중요하다. 단순한 흑백 종이라면 굳이 thresholding을 적용할 필요가 없지만, 다양한 색이 혼합된 경우라면 경계값을 수정하면서 최적의 경계값을 찾아야 한다. 하지만, 반복적인 경계값을 찾는 것은 굉장히 번거롭고 오랜 시간이 소요된다. 이를 극복한 것이 오츠의 이진화 알고리즘이며, 한 번에 효율적인 결과를 얻을 수 있다. cv2.threshold()의 끝에 cv2.THRESH_OTSU만 추가하면 된다. 이를 사용하면 기존에 경계값을 전달하는 인자는 어떤 수를 지정하더라도 무시된다. 그리고, 오츠의 이진화 알고리즘에 의해 선택된 경계값을 ret로 받을 수 있으며 다음과 같이 작성할 수 있다.

 

import cv2
import numpy as np

img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

ret, result = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

print(ret)

cv2.imshow('OTSU', thres)
cv2.waitKey()
cv2.destroyAllWindows()

cv2.THRESH_OTSU 적용

 

위 코드를 수행하게 되면 ret 값은 123.0이 출력된다. 1. 전역 thresholding에서 임계값을 127로 설정했기 때문에 cv2.THRESH_OTSU를 적용한 것과 비교했을 때 binary image가 큰 차이를 보이지는 않는다.

 

3. 적응형 thresholding

평균 값과 가우시안 분포를 적용해서 오츠의 이진화 알고리즘보다 좋은 결과를 얻을 수도 있다. 이미지를 여러 영역으로 나누어 주변의 pixel과 비교해서 경계값을 구하는 것을 적응형 thresholding이라고 한다.

 

import cv2
import numpy as np

img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

thres1 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 3)
thres2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 9, 3)

cv2.imshow('mean', thres1)
cv2.imshow('gaussian', thres2)
cv2.waitKey()
cv2.destroyAllWindows()

평균 threshold(좌) / 가우시안 threshold(우)

 

적응형 thresholding은 cv2.adaptiveThreshold() 함수를 사용한다. 입력 영상, 경계값을 만족하는 픽셀에 적용할 값, 경게값 결정 방법, threshold 적용 방법, 영역으로 나눌 이웃 pixel의 크기, 계산된 경계값에서 가감할 상수가 인자로 구성된다. 그러면, 기존 경계값에서 가감된 값으로 thresholding이 적용된다. 단, 영역으로 나눌 이웃 pixel의 크기는 반드시 1보다 큰 홀수를 사용해야 한다.

 

경계값을 전체 이미지에 적용하는 것을 전역 thresholding, 이미지를 여러 영역으로 분할해서 해당 영역에 맞는 경계값을 찾아 적용하는 것을 지역 thresholding이라고 한다.

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기