二值化的目的,是確定一個像素值,以像素爲分界,將圖像劃分爲前景和背景,前景的像素值取相同值,背景的像素也取相同值,從而將前景和背景的差異,在圖像中最大化,或者說可以突出前景或者背景信息。
二值化可以有效的降低噪聲,並且可以一定程度的增強目標特徵
我使用一下,這篇文章的配圖:https://blog.csdn.net/bigat/article/details/80889636
該文是關於圖像混合的文章,我只是想用這兩張圖來說明,二值化的效果。
那麼最終要的問題就是,選擇哪個像素值,作爲劃分最合適?
日本學者大津(Nobuyuki Otsu)於1979年給出了很好的解答,論文:
算法的核心思想是,選擇使得劃分出來的前景與背景有最大方差的劃分爲最優劃分,很多文章稱爲類間方差,因爲可以將前景數據作爲一類,而背景數據作爲另一類,所以可以稱爲前景類與背景類的類間方差。
所以只要知道,怎麼計算圖像數據的類間方差就可以實現了,這裏引用一下:https://blog.csdn.net/u012198575/article/details/81128799
中的公式,因爲很全,所以我就不重新寫了,如下:
公式: 記 M = 256 單通道灰度分級 Sum = 像素總數
- 背景像素佔比
- 前景像素佔比
- 背景的平均灰度值
- 前景的平均灰度值
- 0~M灰度區間的灰度累計值
- 類間方差:
- 將公式3.4.5帶入公式6 可得最終簡化公式:
下面使用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()