Image Thresholding¶

Goal¶
이 문서에서는 Binary Image에 대해 소개하고,
이를 만드는 기법인
Simple Thresholding,Adaptive Thresholding,Otsu Thresholding등을 같이 소개한다.
OpenCV에서는 관련하여 다음의 함수들을 제공한다.
cv2.thresholdcv2.adaptiveThreshold
Binary Image¶
Pixel의 intensity가 0과 1 두가지 값으로만 구성된 Image.
Thresholding에 의해 형성됨
- Image 전역에 Thresholding을 하는 경우 (Global Thresholding)와
- 이미지의 작은 영역(patch, region)별로 thresholding을 하는 경우(Adaptive method)
로 나눌 수 있음.
주로 특정 object와 background를 나눈 결과물의 형태이며, 이를 나누기 위한 Masking 등에 사용됨.
Simple Thresholding¶
OpenCV는 기본적으로
uint8을 사용하기 때문에,
binary image가 앞서 설명한 0,1이 아닌 0,255를 가진다.
Simple Thresholding은
- 특정 threshold 보다 큰 값은 1에서 255 중 지정된 값으로 할당하고,
- 나머지는 0으로 할당하는 방식으로 동작하며
- OpenCV에서는
cv2.threshold라는 함수를 통해 제공된다.
Global method 라고 생각하면 된다 (전체 이미지에 대해 하나의 threshold를 기준으로 적용한다).
cv2.threshold (src_img, threshold, max_val, style_of_threshold)
src_img: thresholding의 대상이 되는 ndarray 객체.threshold: thresholding의 기준이 되는 값.max_val:threshold보다 높은 intensity를 가지는 pixel들에 할당되는 값 (style_of_threshold에 따라 낮은 값에 할당되기도 함)style_of_threshold: Thresholding 방식의 종류를 지정한다.
다음의 표를 참고.
| Threshold Types | Threshold 초과 | Threshold 이하 |
|---|---|---|
cv2.THRESH_BINARY | max_val | 0 |
cv2.THRESH_BINARY_INV | 0 | max_val |
cv2.THRESH_TRUNC | Threshold | src_img(x,y) |
cv2.THRESH_TOZERO | src_img(x,y) | 0 |
cv2.THRESH_TOZERO_INV | 0 | src_img(x,y) |

이 외에 다음을 선택가능함.
cv2.THRESH_OTSU: Otsu Algorithm으로 자동으로 threshold를 찾고 thresholding.cv2.THRESH_TRINAGLE: Triangle Algorithm으로 자동으로 threshold를 찾고 thresholding.
Example¶
다음 코드는 gradient image (위의 그림에서 Original Image)를 생성함.
import numpy as np
from matplotlib import pyplot as plt
width = 512
height = 256
mask1 = np.repeat(
np.tile(np.linspace(0, 1, width), (height, 1))[:, :, np.newaxis],
3, axis=2)
mask1 = (mask1*255).astype("uint8")
plt.imshow(mask1)
Simple Thresholding의 여러 스타일을 사용하는 예제는 다음과 같음.
import cv2
import numpy as np
from matplotlib import pyplot as plt
# img = cv2.imread('gradient.png',0)
img = mask1.copy()
ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
ret,thresh2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
titles = ['Original Image','BINARY','BINARY_INV',
'TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
plt.figure(figsize=(10,4))
for i in range(6):
plt.subplot(2,3,i+1)
plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
Adaptive Thresholding¶
Global Thresholding은 illumination등이 균일하지 않은 경우에는 좋은 방법이 아니다.
Adaptive thresholding은
- image 각 pixel에 대해 이웃하는 작은 구역들을 구하고,
- 이를 이용해 각 구역 별로 다른 threshold를 적용하는 방식
으로 동작하며,
- images with varying illumination 의 경우
- 보다 나은 Thresholding을 제공한다.
OpenCV 에서는 cv2.adaptiveThreshold 함수를 통해 제공된다.
adaptive_method: 어떤 방식을 사용할지를 고름.cv2.ADAPTIVE_THRESH_MEAN_C:
block(=neighborhood area라고도 불림)의 mean을 threshold로 사용.
\(T(x,y) = \mu_\text{block}(x,y) - C\)cv2.ADAPTIVE_THRESH_GAUSSIAN_C:
neighborhood 들에 대해 Gaussian window 사용한 weighted sum을 구하고 이를 threshold로 사용.
\(T(x,y) = G* \text{N} (x,y)-C\)
block_size: neighborhood area 크기. 3 이상의 홀수여야 함.C- threshold 구할 때 빼주는 일종의 constant.
Example¶
import cv2
import numpy as np
from matplotlib import pyplot as plt
import requests
url = 'https://raw.githubusercontent.com/dsaint31x/OpenCV_Python_Tutorial/master/images/sudoku.jpg'
image_ndarray = np.asarray(bytearray(requests.get(url).content), dtype=np.uint8)
img = cv2.imdecode(image_ndarray, cv2.IMREAD_UNCHANGED)
img = cv2.medianBlur(img,5)
ret,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
cv2.THRESH_BINARY,11,2)
th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
plt.figure(figsize=(12,12))
for i in range(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
Histogram based Thresholding¶
image의 (intensity) histogram 에서
Threshold을 결정하고 이를 이용하여 Thresholding하는 방법.
- 실제로는 Threshold를 구하기가 쉽진 않음.( 명확하지 않은 히스토그램의 골과 마루, 잡음 등)
- 크게 다음으로 나뉨.
- p-타일 기법,
- 모드 기법,
- 최대분산비 기법 (=Otsu's Thresholding, Otsu's binarization)
p-tile¶
- histogram으로 부터 target(대상 물체)가 차지하는 비율(
p%)에 해당하는 intensity를 Threshold로 결정하는 방법 - 대상물체의 비율을 미리 알 수 없기 때문에 매번 사람이 이를 정해야 한다.
mode thresholding¶
- Histogram \(H\)에서 일정 거리 이상 떨어진 local maxima \(i\),\(j\)를 찾는다.
- \(i\)와 \(j\) 사이에서의 minimum value를 갖는 \(k\)를 찾는다.
- \(p=\frac{\text{min} [H(i),H(j)]}{H(k)}\)를 계산한다.
- 최대 \(p\)를 갖는 \(k\)를 threshold로 한다.
1번의 일정거리를 정하기가 좀 난감한 경우가 많다.
Otsu's thresholding (최대분산비 Thresholding) **¶
Global thresholding 에서 가장 많이 사용하는 방법임.
- p-tile, mode thresholding과 달리
- statistical property를 이용하여
- 가장 적절한 global threshold를 알아서 찾아줌.
아쉽지만, Global threshold를 찾는 것으로 local에 따라 다른 thresholding을 적용하는 adaptive thresholding 은 아님.
불균일한 조명에서 얻어진 image에선 성능이 좋지 못함.
Otsu thresholding은 bimodal image (histogram의 2개의 peak를 가지는 경우를 가리킴)에 가장 적절한 threshold를 찾아준다.
Otsu algorithm은 2개의 class로 나눌 때,
- 각 class의
within-class variance를 최소화 하면서 - 두 class의
inter-class variance는 최대로
만드는 threshold를 찾는다.
수식으로 표현하면 다음의 weighted within-class variance를 최소화하는 \(t\)를 찾는다.
where
- \(i\) : intensity (
uint8인 경우, 0~255) - \(I\) : max intensity of image.
- \(P(i)\) : \(i\)를 가지는 pixel 의 갯수 (또는 전체픽셀에서 차지하는 비율).
OpenCV에서 cv2.threshold에서 cv2.THRESH_OTSU를 사용하는 방법은 다음과 같음.
ret,th = cv2.threshold(
src_img,
0, #min을 넣어주면 된다.
255, #max을 넣어주면 된다.
cv2.THRESH_BINARY+cv2.THRESH_OTSU
)
- 사용하고자 하는 thresholding style에
cv2.THRESH_OTSU를 더해주면 됨. - 이 경우, 반환값
ret에 Otsu알고리즘으로 찾은 값이 저장됨.