大律法(OTSU) ——图像数据二值化

2023-11-17

二值化的目的,是确定一个像素值,以像素为分界,将图像划分为前景和背景,前景的像素值取相同值,背景的像素也取相同值,从而将前景和背景的差异,在图像中最大化,或者说可以突出前景或者背景信息。

二值化可以有效的降低噪声,并且可以一定程度的增强目标特征

我使用一下,这篇文章的配图: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, Man and 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()

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

大律法(OTSU) ——图像数据二值化 的相关文章

随机推荐