[opencv-python] 이미지 Thresholding

2019. 7. 5. 18:00Python-이론/python-opencv

이미지 Thresholding

이미지 Thresholding는 문턱 값 이상이면 어떤 값으로 바꾸어주고 낮으면 0으로 바꾸어주는 기능을 합니다. 예전에 전자전기개론때 배운 문턱전압이랑 비슷한 이론인 것 같습니다. 

 

cv2.threshold(img, threshold_value, value, flag)

img:grayScale이고 threshold_value는 픽셀 문턱값이고 문턱값 이상이면 value로 바꾸어줍니다. 

 

flag에서도 다양한 종류가 존재합니다. 

 

cv2.THRESH_BINARY: threshold보다 크면 value이고 아니면 0으로 바꾸어 줍니다. 

cv2.THRESH_BINARY_INV: threshold보다 크면 0이고 아니면 value로 바꾸어 줍니다.   

cv2.THRESH_TRUNC: threshold보다 크면 value로 지정하고 작으면 기존의 값 그대로 사용한다. 

cv2.THRESH_TOZERO: treshold_value보다 크면 픽셀 값 그대로 작으면 0으로 할당한다. 

cv2.THRESH_TOZERO_INV: threshold_value보다 크면 0으로 작으면 그대로 할당해준다. 

 

import numpy as np
import cv2


def setNamedWindow(windowName, type):
    cv2.namedWindow(windowName, type)


def show(windowName, src):
    cv2.imshow(windowName, src)


img = cv2.imread("../resource/Images/ipad.jpeg", cv2.IMREAD_GRAYSCALE)

setNamedWindow("original", cv2.WINDOW_NORMAL)
setNamedWindow("BINARY", cv2.WINDOW_NORMAL)
setNamedWindow("BINARY_INV", cv2.WINDOW_NORMAL)
setNamedWindow("TRUNC", cv2.WINDOW_NORMAL)
setNamedWindow("TOZERO", cv2.WINDOW_NORMAL)
setNamedWindow("TOZERO_INV", cv2.WINDOW_NORMAL)

ret, thr1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret, thr2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret, thr3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
ret, thr4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
ret, thr5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)

show("original", img)
show("BINARY", thr1)
show("BINARY_INV", thr2)
show("TRUNC", thr3)
show("TOZERO", thr4)
show("TOZERO_INV", thr5)

cv2.waitKey(0)
cv2.destroyAllWindows()

결과

 

Adaptive Thresholding

adaptive thresholding은 앞서 설명한 것과 다르게 지역별로 thresholding 값을 구해줘서 적용시키는 방법입니다. 개발자가 정해준 임의의 값이 아닌 bookSize*bookSize에서 구할 수 있기 때문에 더 정확하게 threshold를 적용할 수 있을 것 같습니다. 

코드

import numpy as np
import cv2

img = cv2.imread("../resource/images/ipad.jpeg", cv2.IMREAD_GRAYSCALE)

ret, thr1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
thr2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
thr3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)

title = ['original', 'Global Threshold', 'Adaptive Mean', 'Adaptive Gaussian']
images = [img, thr1, thr2, thr3]

for i in range(4):
    cv2.namedWindow(title[i], cv2.WINDOW_NORMAL)
    cv2.imshow(title[i], images[i])

cv2.waitKey(0)
cv2.destroyAllWindows()

 

cv2.adaptiveThreshold(img, value, adaptiveMethod, thresholdType, blockSize, C)

img: gray 스케일의 값입니다. 

value: 문턱 값 이상이되면 사용되는 value입니다. 

adaptiveMethod: threshold를 구해주는데 사용되는 알고리즘입니다. 

cv2.ADAPTIVE_THRESH_MEAN_C: 적용할 픽셀 (x, y)를 중심으로 하는 bookSize * bookSize안에 있는 픽셀 값의 평균에서 C를 뺀 값입니다. 

cv2.ADATPIVE_THRESH_GAUSSIAN_C: 적용할 픽셀 (x, y)를 중심으로 하는 bookSize * bookSize안에 있는 Gaussian 윈도우 기반 가중치들의 합에서 C를 뺀 값입니다. 

blockSize: 문턱 값을 계산하기 위한 block크기 입니다. 이는 홀수를 해야합니다. 왜냐하면 픽셀이 블럭의 중앙이 되어야합니다. 

C: 보정 상수로 이 값이 양수이면 계산된 adaptive 문턱값에서 빼고 음수이면 더해줍니다. 

 

결과

 

Otsu's Binarization

global thresholding 방법에서 문턱값으로 우리가 정한 임의의 값을 사용했습니다. 그렇지만 최대의 효율의 T값을 찾아야합니다. 이런 T 값을 찾기 위해서는 Otsu's 이진화 방법이 사용될 수 있습니다. Otsu Binarization은 두개의 봉우리 사이의 값을 선택하여 더 개선되게 이진화를 해줄 수 있습니다. 즉 분산을 최소로 만들어줍니다.  

코드

import numpy as np
import cv2
import matplotlib.pyplot as plt


def thresholding():
    img = cv2.imread("../resource/Images/ipad.jpeg", cv2.IMREAD_GRAYSCALE)

    ret, thr1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

    ret, thr2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    blur = cv2.GaussianBlur(img, (5, 5), 0)
    ret, thr3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    titles = [
        'original', 'Histogram', 'G-Thresholding',
        'original', 'Histogram', 'Otsu-Thresholding',
        'Gaussian Filter', 'Histogram', 'Otsu-Thresholding'
    ]

    images = [img, 0, thr1, img, 0, thr2, img, 0, thr3]

    for i in range(3):
        plt.subplot(3, 3, i * 3 + 1), plt.imshow(images[i * 3], 'gray')
        plt.title(titles[i*3]), plt.xticks([]), plt.yticks([])

        plt.subplot(3, 3, i * 3 + 2), plt.hist(images[i * 3].ravel(), 256)
        plt.title(titles[i * 3]), plt.xticks([]), plt.yticks([])

        plt.subplot(3, 3, i * 3 + 3), plt.imshow(images[i * 3+2], 'gray')
        plt.title(titles[i * 3+2]), plt.xticks([]), plt.yticks([])
    plt.show()


thresholding()

ret, thr2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

cv2.THRESH_BINARY + cv2.THRESH_OTSU

cv2.에서는 Otsu Binarization을 적용하는 함수는 따로 없고, cv2.threshold() 함수에 cv2.THRESH_BINARY + cv2.THRESH_OTSU 더하고 문턱값에 0을 전달해주면 됩니다. 이렇게 하면 cv2.threshold() 함수는 적절한 문턱값을 계산한 후 이를 적용한 결과를 리턴합니다. 

 

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

GaussianBlur를 사용하여 노이즈를 제거하여 이미지를 바꾸어 주었습니다. 

 

plt.subplot(3, 3, 3*i+1), plt.imshow(images[i*3], 'gray')

matplotlib을 통해 view를 만들어 주었습니다.

 

결과