Otsu Binarization

Bu yazıda Otsu Binarization eşikleme uygulamasından bahsedeceğim.

Global eşiklemede kendimiz belirlediğimiz bir eşik değeri vardı fakat görüntüyü işlemeye başladığımızda verimli olacak mı olmayacak mı tam olarak bilmiyoruz yani deneme yanımla yoluyla bulmak zorundayız. Otsu’nun yaptığı şeyi açıklayacak olursak.. Histogram çıktısında iki tepesi olan yani bimodal görüntüyü düşünelim, Otsu da bu iki tepe arasındaki değeri threshold olarak alarak işlemi gerçekleştirir. Yani bimodal olmayan görüntülerde Otsu pek fazla yararlı olmayacaktır.

Otsu’yu uygulamak için cv2.threshold fonksiyonunda flag olarak cv2.THRESH_OTSU ‘yu kullanacağız ve threshold değerini 0 olarak girip geçeceğiz. Kullandığıımız Otsu algoritması bize bulduğu optimal threshold değerini ikinci bir output olarak sunar. Eğer Otsu’yu kullanmadıysanız sizin verdiğiniz değeri geri döndürecektir.

Başka bir yerden bulduğum aşağıdaki örneğe bir göz atalım. İlk olarak global thresholding, ikinci olarak da Otsu uygulanmış. Sonrasında ise gaussian kernel yapılıp Otsu uygulanmış, sonuçlara baktığımızda doğru kullanıldıktan sonra Otsu ile pürüssüze yakın bir sonuç elde edebiliyoruz.

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

img = cv2.imread('noisy2.png',0)

# global thresholding
ret1,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)

# Otsu's thresholding
ret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# Otsu's thresholding after Gaussian filtering
blur = cv2.GaussianBlur(img,(5,5),0)
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# plot all the images and their histograms
images = [img, 0, th1,
          img, 0, th2,
          blur, 0, th3]
titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)',
          'Original Noisy Image','Histogram',"Otsu's Thresholding",
          'Gaussian filtered Image','Histogram',"Otsu's Thresholding"]

for i in xrange(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+1]), 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()

Otsu Binarization Nasıl çalışır?

Şimdi de Otsu algoritmasının yapısını inceleyelim. Otsu algoritması aşağıdaki bağıntıyla sınıf içi varyans ağırlığını minimize ederek threshold değerini bulmaya çalışır.

where

İki tepe arasında iki sınıf içinde varyansının minimum olduğu bir t değeri bulur. Otsu algoritması Python2 kodunu aşağıda görebilirsiniz.

img = cv2.imread('noisy2.png',0)
blur = cv2.GaussianBlur(img,(5,5),0)

# find normalized_histogram, and its cumulative distribution function
hist = cv2.calcHist([blur],[0],None,[256],[0,256])
hist_norm = hist.ravel()/hist.max()
Q = hist_norm.cumsum()

bins = np.arange(256)

fn_min = np.inf
thresh = -1

for i in xrange(1,256):
    p1,p2 = np.hsplit(hist_norm,[i]) # probabilities
    q1,q2 = Q[i],Q[255]-Q[i] # cum sum of classes
    b1,b2 = np.hsplit(bins,[i]) # weights

    # finding means and variances
    m1,m2 = np.sum(p1*b1)/q1, np.sum(p2*b2)/q2
    v1,v2 = np.sum(((b1-m1)**2)*p1)/q1,np.sum(((b2-m2)**2)*p2)/q2

    # calculates the minimization function
    fn = v1*q1 + v2*q2
    if fn < fn_min:
        fn_min = fn
        thresh = i

# find otsu's threshold value with OpenCV function
ret, otsu = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
print thresh,ret