大律法(OTSU) ——圖像數據二值化

二值化的目的,是確定一個像素值,以像素爲分界,將圖像劃分爲前景和背景,前景的像素值取相同值,背景的像素也取相同值,從而將前景和背景的差異,在圖像中最大化,或者說可以突出前景或者背景信息。

二值化可以有效的降低噪聲,並且可以一定程度的增強目標特徵

我使用一下,這篇文章的配圖:https://blog.csdn.net/bigat/article/details/80889636

該文是關於圖像混合的文章,我只是想用這兩張圖來說明,二值化的效果。

 那麼最終要的問題就是,選擇哪個像素值,作爲劃分最合適?

日本學者大津(Nobuyuki Otsu)於1979年給出了很好的解答,論文:

OTSU N. A threshold selection method from gray-level histo- grams[J].IEEE Transactions on Systems,Manand Cybernetics, 1979,9( 1) : 62 - 66.
 

算法的核心思想是,選擇使得劃分出來的前景與背景有最大方差的劃分爲最優劃分,很多文章稱爲類間方差,因爲可以將前景數據作爲一類,而背景數據作爲另一類,所以可以稱爲前景類與背景類的類間方差。

所以只要知道,怎麼計算圖像數據的類間方差就可以實現了,這裏引用一下:https://blog.csdn.net/u012198575/article/details/81128799

中的公式,因爲很全,所以我就不重新寫了,如下:

公式:  記 M = 256 單通道灰度分級 Sum = 像素總數

  1. 背景像素佔比 \omega1 = \frac{N1}{Sum}  
  2. 前景像素佔比\omega2 = 1- \omega1 = \frac{N2}{Sum} =1- \frac{N1}{Sum}
  3. 背景的平均灰度值\mu 1 = \sum_{i = 0}^{t} i *Pr(i | C_{0}) = \sum_{i = 0}^{t} i *Pi / \sum_{i = 0}^{t} Pi = \frac{\mu(t))}{\omega_{1}}
  4. 前景的平均灰度值\mu 2 = \sum_{i = t+1}^{M - 1} i *Pr(i | C_{1}) = \sum_{i = t+1}^{M - 1} i *Pi / \sum_{i = t+1}^{M - 1} Pi = \frac{\mu - \mu(t))}{\omega _{2}}
  5. 0~M灰度區間的灰度累計值\mu = \mu1*\omega 1 + \mu2*\omega 2
  6. 類間方差:g = \omega 1 * (\mu - \mu1)^{2} + \omega 2 * (\mu - \mu2)^{2}
  7. 將公式3.4.5帶入公式6 可得最終簡化公式: g = \omega 1 * \omega2 * (\mu1 - \mu2)^{2}

下面使用Python+cv2來實現,並且和cv2中的OTSU的結果進行比較,使用cv2主要是爲了讀取圖片:

將三通道的RGB圖像轉換爲單通道的灰度圖像:

公式如下I[i][j] = (299*r[i][j] + 587*g[i][j] + 114*b[i][j])/1000

這裏不做過多解釋,可以使用cv2的方法進行轉換,這裏是自己實現的算法:

def Gray(img):
    a = np.shape(img)
    r,g,b = cv2.split(img)
    img_new = np.zeros((a[0], a[1]))
    for i in range(a[0]):
        for j in range(a[1]):
            data = (299*r[i][j] + 587*g[i][j] + 114*b[i][j])/1000
            img_new[i][j] = data
    img_new = img_new.astype('uint8')
    return img_new

效果如下:

 統計各個灰度值像素的數量和佔比:

def Pixel_num(img):
    num = [0 for _ in range(256)]
    a = np.shape(img)
    for i in range(a[0]):
        for j in range(a[1]):
            num[img[i][j]] += 1
    return num

def Pixel_rate(num_list):
    rate_list = []
    n = sum(num_list)
    for i in range(len(num_list)):
        rate = num_list[i] / n
        rate_list.append(rate)
    return rate_list

遍歷0~255的像素值,尋找最優:

def Optimal_partition(rate_list):
    deltaMax = 0
    T = 0
    for i in range(256):
        w1 = w2 = u1 = u2 = 0
        u1tmp = u2tmp = 0
        deltaTmp = 0
        for j in range(256):
            if (j <= i):
                w1 += rate_list[j]
                u1tmp += j * rate_list[j]
            else:
                w2 += rate_list[j]
                u2tmp += j * rate_list[j]
        if w1 == 0:
            u1 = 0
        else:
            u1 = u1tmp / w1
        if w2 == 0:
            u2 = 0
        else:
            u2 = u2tmp / w2
        deltaTmp = w1 * w2 * ((u1- u2) ** 2)
        if deltaTmp > deltaMax:
            deltaMax = deltaTmp
            T = i
    return T

根據最優灰度值進行劃分:

def Otsu(img, T):
    a = np.shape(img)
    new_img = np.zeros((a[0], a[1]))
    for i in range(a[0]):
        for j in range(a[1]):
            if img[i][j] > T:
                new_img[i][j] = 255
            else:
                new_img[i][j] = 0
    return new_img

 結果如下:

cv2結果:

ret, th = cv2.threshold(new_img, 0, 255, cv2.THRESH_OTSU)

 效果一致

使用面向對象的方式將算法封裝爲類(文件名爲Threshold):

'''
@Author: BTboay
@Date: 2019-12-05 12:28:10
@LastEditTime: 2019-12-05 14:28:25
@LastEditors: Please set LastEditors
@Description: In User Settings Edit
@FilePath: \YOLOv3_01\OTSU.py
'''
import numpy as np
import cv2

class OTSU():
    def __init__(self, img_path):
        img = cv2.imread(img_path)
        self.img = img
        img_gray = self.Gray()
        self.img = img_gray
        num_list = self.Pixel_num()
        self.num_list = num_list
        rate_list = self.Pixel_rate()
        self.rate_list = rate_list
        optimal_pixel = self.Optimal_partition()
        self.optimal_pixel = optimal_pixel

    def Gray(self):
        a = np.shape(self.img)
        r,g,b = cv2.split(self.img)
        img_new = np.zeros((a[0], a[1]))
        for i in range(a[0]):
            for j in range(a[1]):
                data = (299*r[i][j] + 587*g[i][j] + 114*b[i][j])/1000
                img_new[i][j] = data
        img_new = img_new.astype('uint8')
        return img_new

    def Pixel_num(self):
        num = [0 for _ in range(256)]
        a = np.shape(self.img)
        for i in range(a[0]):
            for j in range(a[1]):
                num[self.img[i][j]] += 1
        return num

    def Pixel_rate(self):
        rate_list = []
        n = sum(self.num_list)
        for i in range(len(self.num_list)):
            rate = self.num_list[i] / n
            rate_list.append(rate)
        return rate_list

    def Optimal_partition(self):
        deltaMax = 0
        T = 0
        for i in range(256):
            w1 = w2 = u1 = u2 = 0
            u1tmp = u2tmp = 0
            deltaTmp = 0
            for j in range(256):
                if (j <= i):
                    w1 += self.rate_list[j]
                    u1tmp += j * self.rate_list[j]
                else:
                    w2 += self.rate_list[j]
                    u2tmp += j * self.rate_list[j]
            if w1 == 0:
                u1 = 0
            else:
                u1 = u1tmp / w1
            if w2 == 0:
                u2 = 0
            else:
                u2 = u2tmp / w2
            deltaTmp = w1 * w2 * ((u1- u2) ** 2)
            if deltaTmp > deltaMax:
                deltaMax = deltaTmp
                T = i
        return T

    def Otsu(self):
        a = np.shape(self.img)
        new_img = np.zeros((a[0], a[1]))
        for i in range(a[0]):
            for j in range(a[1]):
                if self.img[i][j] > self.optimal_pixel:
                    new_img[i][j] = 255
                else:
                    new_img[i][j] = 0
        return new_img

調用該類:

'''
@Author: your name
@Date: 2019-12-03 15:43:11
@LastEditTime: 2019-12-05 15:20:49
@LastEditors: Please set LastEditors
@Description: In User Settings Edit
@FilePath: gray.py
'''
import numpy as np
import matplotlib.pyplot as plt
import cv2
import Threshold

if __name__ == "__main__":
    path = 'D:/WorkSpace/YOLOv3_01/05.jpg'
    a = Threshold.OTSU(path)
    new_img = a.Otsu()

    plt.imshow(new_img, 'gray')
    plt.axis('off')
    plt.show()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章