本文的内容来自与十三种基于直方图的图像全局二值化算法原理、实现、代码及效果。本文的中的算法是基于图像的灰度直方图进行阈值分割。
所谓图像的灰度直方图是在将图像进行灰度处理后,计算灰度值的分布。也就是每个灰度值在灰度图像中有多少个点。一般情况下,图像灰度值的取值范围为0~255,所以灰度直方图是一个有256个元素的数组。
阈值分割就是找到一个合适阈值,对灰度图像进行处理。大于阈值的设置为255,小于阈值的设置为0。也可以根据需要设为其他数值。这样处理灰度图像,可以将前景与背景做一个区分。
此方法的关键就是计算阈值。在前面的链接中,介绍了13种方法并提供了C语言实现。本文将这些算法用Python进行了重写。
灰度平均值是图像总像素值/总像素数:
总 像 素 值 = ∑ g = 0 255 g × h ( g ) 总像素值=\displaystyle \sum_{g=0}^{255}g\times h(g) 总像素值=g=0∑255g×h(g)
总 像 素 数 = ∑ g = 0 255 h ( g ) 总像素数=\displaystyle \sum_{g=0}^{255}h(g) 总像素数=g=0∑255h(g)
总像素数其实就是图像的宽度X图像的长度,也是图像的大小。
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def GetMeanThreshold(H):
Amount = np.sum(H)
Gray_Sum = np.sum(H*np.arange(256))
return int(float(Gray_Sum/Amount))
def GrayThreshold(image, maxval=255):
g = GrayHist(image)
thresh = GetMeanThreshold(g)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, img_new = GrayThreshold(img_gray)
th1, img_new_1 = cv2.threshold(img_gray, 146, 255, cv2.THRESH_BINARY)
print(th, th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()
此方法先设定一个阈值p(先验概率),比如50%。然后从0开始对灰度值求和。计算到灰度值Y时,[0~Y]的综合>=总像素值*p,则设定灰度值Y为阈值。
该方法需要根据先验概率确定一个百分比。人的经验很重要。
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def GetPTileThreshold(H,ptile=0.5):
Amount = np.sum(H)
A = Amount * ptile
psum = 0
for Y in range(256):
psum += H[Y]
if psum >= A:
return Y
return -1 # 没有符合条件的阈值
def GrayThreshold(image, maxval=255):
g = GrayHist(image)
thresh = GetPTileThreshold(g,0.25)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, img_new = GrayThreshold(img_gray)
th1, img_new_1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
print(th, th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()
当百分比设为25%时,阈值为123。这种方法需要认为调节阈值,否则很难得到理想的结果。
此方法适合与有明显双峰值的图像,对于单峰值或者平坦的灰度直方图形并不适合。
其原理是通过迭代对直方图数据进行判断,判端是否是一个双峰的直方图。如果不是,则对直方图数据进行窗口为3的平滑。如果迭代了一定的数量比如1000次后仍未获得一个双峰的直方图,则函数执行失败。如成功获得,则最终阈值取两个双峰之间的谷底值作为阈值。
所谓窗口为3的平滑,就是将灰度p的值,用p-1,p和p+1的值进行平均。
直方图通过迭代形成双峰后,有2种方法获得阈值:
(1)双峰的平均值
(2)双峰之间的谷底值
第一种方法是找到双峰后,取平均值的整数。第二种方法找到谷底的最小值。
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def GetIntermodesThreshold(H,method=0):
Iter = 0
HistGramC = np.array(H, dtype=np.float64) # 基于精度问题,一定要用浮点数来处理,否则得不到正确的结果
HistGramCC = np.array(H, dtype=np.float64) # 求均值的过程会破坏前面的数据,因此需要两份数据
# 通过三点求均值来平滑直方图
while IsDimodal(HistGramCC) == False: # 判断是否已经是双峰的图像了
HistGramCC[0] = (HistGramC[0] + HistGramC[0] + HistGramC[1]) / 3 # 第一点
for Y in range(1, 255):
HistGramCC[Y] = (HistGramC[Y - 1] + HistGramC[Y] + HistGramC[Y + 1]) / 3 # 中间的点
HistGramCC[255] = (HistGramC[254] + HistGramC[255] + HistGramC[255]) / 3 # 最后一点
HistGramC = np.array(HistGramCC, dtype=np.float64) # 备份数据
Iter += 1
if Iter >= 1000:
return -1 # 直方图无法平滑为双峰的,返回错误代码
if method > 0:
Peakfound = False
for Y in range(1,255):
if HistGramCC[Y - 1] < HistGramCC[Y] and HistGramCC[Y + 1] < HistGramCC[Y]:
Peakfound = True
if Peakfound and HistGramCC[Y - 1] >= HistGramCC[Y] and HistGramCC[Y + 1] >= HistGramCC[Y]:
return Y - 1
return -1
else:
# 阈值为两峰值的平均值
Peak = np.zeros(2,dtype=np.uint16)
Index = 0
for Y in range(1, 255):
if HistGramCC[Y - 1] < HistGramCC[Y] and HistGramCC[Y + 1] < HistGramCC[Y]:
Peak[Index] = Y - 1
Index += 1
return int((Peak[0] + Peak[1]) / 2)
def IsDimodal(H): # 检测直方图是否为双峰的
# 对直方图的峰进行计数,只有峰数位2才为双峰
Count = 0
for Y in range(1, 255):
if H[Y - 1] < H[Y] and H[Y + 1] < H[Y]:
Count += 1
if Count > 2: return False
if Count == 2:
return True
else:
return False
def GrayThreshold(image, maxval=255):
g = GrayHist(img_gray)
thresh = GetIntermodesThreshold(g,1)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th1, img_new_1 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_TRIANGLE)
th, img_new = GrayThreshold(img_gray)
print(th, th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()
此种方法得到的小鸟图像阈值为159。原始直方图和平滑后的直方图(黄色)如图:
该方法是查找双峰之间的最小值。使用此方法需要调用GetIntermodesThreshold(g,1)。最后一个参数设置为1即可。
此方法是先从左边找到HistGram中第一个数值不为0的点min,再从右边找到第一个数值不为0的点max。然后取阈值threshold为此2点的中间值。通过计算[min,threshold]和(threshold,max]平均值灰度值对应的点,进行迭代直到平均灰度值点与阈值点相等。
平均灰度值计算为:
G m i n = ∑ g = m i n T k g × h ( g ) ∑ g = m i n T k h ( g ) G_{min}=\frac{\sum_{g=min}^{T_k}g\times h(g)}{\sum_{g=min}^{T_k}h(g)} Gmin=∑g=minTkh(g)∑g=minTkg×h(g)
G m a x = ∑ g = T k m a x g × h ( g ) ∑ g = T k m a x h ( g ) G_{max}=\frac{\sum_{g=T_k}^{max}g\times h(g)}{\sum_{g=T_k}^{max}h(g)} Gmax=∑g=Tkmaxh(g)∑g=Tkmaxg×h(g)
T k + 1 = G m i n + G m a x 2 T_{k+1}=\frac{G_{min}+G_{max}}{2} Tk+1=2Gmin+Gmax
重复进行运算,直到Tk=Tk+1。
需要注意的是可能存在不收敛的情况。
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def GetIterativeBestThreshold(HistGram):
Iter = 0
value = np.where(HistGram > 0)[0] # 非0下标
index = len(value)
if index == 0:
return 0
elif index == 1 or index == 2:
return value[0]
else:
MinValue = value[0]
MaxValue = value[-1]
Threshold = MinValue
NewThreshold = int(MaxValue + MinValue) >> 1
while Threshold != NewThreshold: # 当前后两次迭代的获得阈值相同时,结束迭代
Threshold = NewThreshold
#[MinValue,Threshold]灰度平均值对应的点
SUmInteralOne = np.sum(HistGram[MinValue:Threshold + 1] * np.arange(MinValue, Threshold + 1))
SumOne = np.sum(HistGram[MinValue:Threshold + 1])
MeanValueOne = SUmInteralOne / SumOne
# [Threshold+1,MaxValue]灰度平均值对应的点
SUmInteralTwo = np.sum(HistGram[Threshold + 1:MaxValue + 1] * np.arange(Threshold + 1, MaxValue + 1))
SumTwo = np.sum(HistGram[Threshold + 1:MaxValue + 1])
MeanValueTwo = SUmInteralTwo / SumTwo
NewThreshold = int(MeanValueOne + MeanValueTwo) >> 1 # 求出新的阈值
Iter += 1
if Iter >= 1000:
return -1
return Threshold
def GrayThreshold(image, maxval=255):
g = GrayHist(image)
thresh = GetIterativeBestThreshold(g)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, img_new = GrayThreshold(img_gray)
th1, img_new_1 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE)
print(th, th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()
此方法求出的阈值为126。
大津法是日本学者大津在1979年提出的方法,也叫最大类间法。其对单峰值或者比较平坦的灰度值有很好的效果,对双峰或者多峰的情况则较差。其方法如下:
记t为前景与背景的分割阈值,前景点数占图像比例为w0,平均灰度为u0;背景点数占图像比例为w1,平均灰度为u1。
则图像的总平均灰度为:u=w0 *u0+w1*u1。
前景和背景图象的方差:g=w0*(u0-u)*(u0-u)+w1*(u1-u)*(u1-u)=w0*w1*(u0-u1)2,此公式为方差公式。
不断的迭代求解出最大的方差值,可以认为此时前景和背景的相差最大,从而得到分割图像的阈值。
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def GetOSTUThreshold(H):
# 判断只有1种灰度、2种灰度以及多种灰度
V = np.where(H > 0)[0] # 非0下标
I = len(V)
if I == 0: return 0
if I == 1: return V[0]
if I == 2: return V[0] if H[V[0]] < H[V[1]] else V[1]
MinValue = V[0]
MaxValue = V[-1]
PixelBack = 0
PixelIntegralBack = 0
Threshold = 0
Amount = np.sum(H)
PixelIntegral = np.sum(H * np.arange(256))
SigmaB = -1
for Y in range(MinValue, MaxValue):
PixelBack = PixelBack + H[Y]
PixelFore = Amount - PixelBack
OmegaBack = PixelBack / Amount
OmegaFore = PixelFore / Amount
PixelIntegralBack += H[Y] * Y
PixelIntegralFore = PixelIntegral - PixelIntegralBack
MicroBack = PixelIntegralBack / PixelBack
MicroFore = PixelIntegralFore / PixelFore
Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore)
if Sigma > SigmaB:
SigmaB = Sigma
Threshold = Y
return Threshold
def GrayThreshold(image, maxval=255):
g = GrayHist(image)
thresh = GetOSTUThreshold(g)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, img_new = GrayThreshold(img_gray)
th1, img_new_1 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print(th, th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()
自定义的OTSU方法与opencv中的OTSU方法得到的阈值是一样的,都是125。
最大熵的思想是将图像分为两部分(可以认为是前景和背景),循环计算两部分的灰度值的熵,使其和最大。取得最大值的灰度即为阈值。
其中熵的概念为:
h i s t = h ( g ) ∑ x = 0 255 h ( x ) + 最 小 正 浮 点 值 hist=\frac{h(g)}{\sum_{x=0}^{255}h(x)}+最小正浮点值 hist=∑x=0255h(x)h(g)+最小正浮点值
S 1 = ∑ g = 0 T h i s t ( g ) S_1=\sum_{g=0}^{T}hist(g) S1=∑g=0Thist(g)
E 1 = − ∑ g = 0 T h i s t ( g ) S 1 × l o g ( ∑ g = 0 T h i s t ( g ) S 1 ) E_1=-\frac{\sum_{g=0}^{T}hist(g)}{S_1}\times log(\frac{\sum_{g=0}^{T}hist(g)}{S_1}) E1=−S1∑g=0Thist(g)×log(S1∑g=0Thist(g))
E 2 = − ∑ g = T + 1 255 h i s t ( g ) 1 − S 1 × l o g ( ∑ g = T 255 h i s t ( g ) 1 − S 1 ) E_2=-\frac{\sum_{g=T+1}^{255}hist(g)}{1-S_1}\times log(\frac{\sum_{g=T}^{255}hist(g)}{1-S_1}) E2=−1−S1∑g=T+1255hist(g)×log(1−S1∑g=T255hist(g))
计算出最大的E1+E2的灰度值即为阈值。
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
import math
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def Get1DMaxEntropyThreshold(H):
V = np.where(H > 0)[0] # 非0下标
I = len(V)
if I == 0: return 0
if I == 1: return V[0]
if I == 2: return V[0] if H[V[0]] < H[V[1]] else V[1]
MinValue = V[0]
MaxValue = V[-1]
Threshold=MinValue
HistGramD = np.zeros(256,dtype=np.float64)
Amount = np.sum(H[MinValue:MaxValue + 1])
eps = np.finfo(np.float64).eps
HistGramD[MinValue:MaxValue+1] = H[MinValue:MaxValue+1]/Amount + eps
MaxEntropy = 0.0
for Y in range(MinValue+1,MaxValue):
SumIntegral = 0.
for X in range(MinValue,Y+1):
SumIntegral += HistGramD[X]
EntropyBack = 0.
for X in range(MinValue,Y+1):
EntropyBack += -HistGramD[X] / SumIntegral * np.log(HistGramD[X] / SumIntegral)
EntropyFore = 0.
for X in range(Y+1,MaxValue+1):
EntropyFore += -HistGramD[X] / (1 - SumIntegral) * np.log(HistGramD[X] / (1 - SumIntegral))
if MaxEntropy < EntropyBack + EntropyFore:
Threshold = Y
MaxEntropy = EntropyBack + EntropyFore
return Threshold
def GrayThreshold(image,maxval=255):
g = GrayHist(image)
thresh = Get1DMaxEntropyThreshold(g)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, img_new = GrayThreshold(img_gray)
th1,img_new_1 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE)
print(th,th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()
计算出的阈值为104。
力矩法的论文可以从此链接中获得力矩法论文。上述论文是英文版的。此方法比较复杂。
实现方法如下:
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
import math
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def AH(H, index):
return np.sum(H[:index+1])
def BH(H, index):
K = np.arange(index+1)
return np.sum(H[:index+1]*K)
def CH(H, index):
K = np.float_power(np.arange(index+1),2)
return np.sum(H[:index + 1] * K)
def DH(H, index):
K = np.float_power(np.arange(index+1),3)
return np.sum(H[:index + 1] * K)
def GetMomentPreservingThreshold(H):
V = np.where(H > 0)[0] # 非0下标
I = len(V)
if I == 0: return 0
if I == 1: return V[0]
if I == 2: return V[0] if H[V[0]] < H[V[1]] else V[1]
MaxValue = V[-1]
Avec = np.zeros(256, dtype=np.float64)
Amount = np.sum(H)
for Y in range(256):
Avec[Y] = AH(H, Y) / Amount
Index = 0
B = BH(H,255)
A = AH(H,255)
C = CH(H,255)
D = DH(H,255)
X2 = (B * C - A * D) / (A * C - B * B)
X1 = (B * D - C * C) / (A * C - B * B)
X0 = 0.5 - (B / A + X2 / 2) / math.sqrt(X2 * X2 - 4 * X1)
Min = float(MaxValue)
for Y in range(256):
if math.fabs(Avec[Y] - X0) < Min:
Min = math.fabs(Avec[Y] - X0)
Index = Y
return int(Index)
def GrayThreshold(image, maxval=255):
g = GrayHist(image)
thresh = GetMomentPreservingThreshold(g)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, img_new = GrayThreshold(img_gray)
th1, img_new_1 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE)
print(th, th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()
具体方法参考基于模糊集理论的一种图像二值化算法的原理、实现效果及代码
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
import math
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def GetHuangFuzzyThreshold(H):
Threshold = -1
BestEntropy = np.finfo(np.float32).max
# 找到第一个和最后一个非0的色阶值
V = np.where(H > 0)[0] # 非0下标
I = len(V)
if I == 0: return 0
if I == 1: return V[0]
if I == 2: return V[0] if H[V[0]] < H[V[1]] else V[1]
First = V[0]
Last = V[-1]
# 计算累计直方图以及对应的带权重的累计直方图
S = np.zeros(Last+1,dtype=np.int64)
W = np.zeros(Last+1,dtype=np.int64)
S[0] = H[0]
start = First if First>1 else 1
for Y in range(start,Last+1):
S[Y] = S[Y - 1] + H[Y]
W[Y] = W[Y - 1] + Y * H[Y]
# 建立公式(4)及(6)所用的查找表
Smu = np.zeros(Last+1-First,dtype=np.float32)
for Y in range(1,Last+1-First):
mu = 1. / (1. + float(Y) / (Last - First)) # 公式(4)
Smu[Y] = -mu * math.log(mu) - (1 - mu) * math.log(1 - mu) # 公式(6)
# 迭代计算最佳阈值
for Y in range(First,Last+1):
Entropy = 0
mu = int(np.round(float(W[Y] / S[Y]))) # 公式17
for X in range(First,Y+1):
Entropy += Smu[abs(X - mu)] * H[X]
mu = int(np.round(float((W[Last] - W[Y]) / (S[Last] - S[Y])))) if Y < Last else 0
for X in range(Y + 1,Last+1):
Entropy += Smu[abs(X - mu)] * H[X] # 公式8
if BestEntropy > Entropy:
BestEntropy = Entropy #取最小熵处为最佳阈值
Threshold = Y
return Threshold
def GrayThreshold(image, maxval=255):
g = GrayHist(image)
thresh = GetHuangFuzzyThreshold(g)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, img_new = GrayThreshold(img_gray)
th1, img_new_1 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE)
print(th, th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()
具体方法见Kittler, J & Illingworth, J (1986), “Minimum error thresholding”, Pattern Recognition 19: 41-47
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
import math
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def GetKittlerMinError(HistGram):
value = np.where(HistGram>0)[0] #非0下标
index = len(value)
if index == 0:
return 0
elif index == 1:
return value[0]
elif index == 2:
return value[0]
else:
MinValue = value[0]
MaxValue = value[-1]
Threshold = -1
MinSigma = np.finfo(np.float64).eps
for Y in range(MinValue,MaxValue):
PixelBack = 0
PixelFore = 0
OmegaBack = 0
OmegaFore = 0
for X in range(MinValue,Y+1):
PixelBack += HistGram[X]
OmegaBack = OmegaBack + X * HistGram[X]
for X in range(Y+1,MaxValue+1):
PixelFore += HistGram[X]
OmegaFore = OmegaFore + X * HistGram[X]
OmegaBack = OmegaBack / PixelBack
OmegaFore = OmegaFore / PixelFore
SigmaBack = 0
SigmaFore = 0
for X in range(MinValue,Y+1):
SigmaBack = SigmaBack + (X - OmegaBack) * (X - OmegaBack) * HistGram[X]
for X in range(Y+1,MaxValue):
SigmaFore = SigmaFore + (X - OmegaFore) * (X - OmegaFore) * HistGram[X]
if SigmaBack == 0 or SigmaFore == 0:
if Threshold == -1:
Threshold = Y
else:
SigmaBack = math.sqrt(SigmaBack / PixelBack)
SigmaFore = math.sqrt(SigmaFore / PixelFore)
Sigma = 1 + 2 * (PixelBack * math.log(SigmaBack / PixelBack) + PixelFore * math.log(SigmaFore / PixelFore))
if Sigma < MinSigma:
MinSigma = Sigma
Threshold = Y
return Threshold
def GrayThreshold(image,maxval=255):
g = GrayHist(image)
thresh = GetKittlerMinError(g)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, img_new = GrayThreshold(img_gray)
th1,img_new_1 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE)
print(th,th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()
参考论文:
Ridler, TW & Calvard, S (1978), “Picture thresholding using an iterative selection method”, IEEE Transactions on Systems, Man and Cybernetics 8: 630-632, http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=4310039
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
import math
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def GetIsoDataThreshold(H):
V = np.where(H > 0)[0] # 非0下标
I = len(V)
if I == 0: return 0
if I == 1: return V[0]
if I == 2: return V[0] if H[V[0]] < H[V[1]] else V[1]
MinValue = V[0]
MaxValue = V[-1]
threshold = MinValue
while (True):
SumOne = 0
SumInteralOne = 0
SumTwo = 0
SumInteralTwo = 0
for i in range(threshold):
SumOne += H[i]
SumInteralOne += H[i] * i
for i in range(threshold+1,MaxValue):
SumTwo += H[i]
SumInteralTwo += (H[i] * i)
if SumOne > 0 and SumTwo > 0:
SumInteralOne /= SumOne
SumInteralTwo /= SumTwo
if threshold == int(np.round((SumInteralOne + SumInteralTwo) / 2.0)):
break
threshold += 1
if threshold > 253:
return 0
return threshold
def GrayThreshold(image,maxval=255):
g = GrayHist(image)
thresh = GetIsoDataThreshold(g)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, img_new = GrayThreshold(img_gray)
th1,img_new_1 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE)
print(th,th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()
参考论文:
Shanbhag, Abhijit G. (1994), "Utilization of information measure as a means of image thresholding", Graph. Models Image Process. (Academic Press, Inc.) 56 (5): 414--419, ISSN 1049-9652, DOI 10.1006/cgip.1994.1037
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
import math
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def GetShanbhagThreshold(H):
V = np.where(H > 0)[0] # 非0下标
I = len(V)
if I == 0: return 0
if I == 1: return V[0]
if I == 2: return V[0] if H[V[0]] < H[V[1]] else V[1]
P1 = np.zeros(256, dtype=np.float64)
P2 = np.zeros(256, dtype=np.float64)
Amount = np.sum(H)
norm_histo = H / Amount
P1[0] = norm_histo[0]
P2[0] = 1.0 - P1[0]
for ih in range(1, 256):
P1[ih] = P1[ih - 1] + norm_histo[ih]
P2[ih] = 1.0 - P1[ih]
first_bin = 0
for ih in range(1, 256):
if not (math.fabs(P1[ih]) < np.finfo(np.float64).eps):
first_bin = ih
break
last_bin = 255
for ih in range(255, first_bin - 1, -1):
if not (math.fabs(P2[ih]) < np.finfo(np.float64).eps):
last_bin = ih
break
threshold = -1
min_ent = np.finfo(np.float64).max
for it in range(first_bin, last_bin + 1):
ent_back = 0.0
term = 0.5 / P1[it]
for ih in range(1, it + 1):
ent_back -= norm_histo[ih] * math.log(1.0 - term * P1[ih - 1]);
ent_back *= term
ent_obj = 0.0
term = 0.5 / P2[it]
for ih in range(it + 1, 256):
ent_obj -= norm_histo[ih] * math.log(1.0 - term * P2[ih])
ent_obj *= term
tot_ent = math.fabs(ent_back - ent_obj)
if tot_ent < min_ent:
min_ent = tot_ent
threshold = it
return threshold
def GrayThreshold(image, maxval=255):
g = GrayHist(image)
thresh = GetShanbhagThreshold(g)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, img_new = GrayThreshold(img_gray)
th1, img_new_1 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE)
print(th, th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()
参考论文:
1) Yen J.C., Chang F.J., and Chang S. (1995) "A New Criterion for Automatic Multilevel Thresholding" IEEE Trans. on Image Processing, 4(3): 370-378
2) Sezgin M. and Sankur B. (2004) "Survey over Image Thresholding Techniques and Quantitative Performance Evaluation" Journal of Electronic Imaging, 13(1): 146-165
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as plt
import math
def GrayHist(img):
grayHist = np.zeros(256, dtype=np.uint64)
for v in range(256):
grayHist[v] = np.sum(img == v)
return grayHist
def GetYenThreshold(H):
V = np.where(H > 0)[0] # 非0下标
I = len(V)
if I == 0: return 0
if I == 1: return V[0]
if I == 2: return V[0] if H[V[0]] < H[V[1]] else V[1]
P1 = np.zeros(256, dtype=np.float64)
P1_sq = np.zeros(256, dtype=np.float64)
P2_sq = np.zeros(256, dtype=np.float64)
norm_histo = H/ np.sum(H)
P1[0] = norm_histo[0]
for ih in range(1, 256):
P1[ih] = P1[ih - 1] + norm_histo[ih]
P1_sq[0] = norm_histo[0] * norm_histo[0]
for ih in range(1, 256):
P1_sq[ih] = P1_sq[ih - 1] + norm_histo[ih] * norm_histo[ih]
P2_sq[255] = 0.0
for ih in range(254, -1, -1):
P2_sq[ih] = P2_sq[ih + 1] + norm_histo[ih + 1] * norm_histo[ih + 1]
threshold = -1
max_crit = np.finfo(np.float64).eps
for it in range(256):
l1 = math.log(P1_sq[it] * P2_sq[it]) if P1_sq[it] * P2_sq[it] > 0.0 else 0.0
l2 = math.log(P1[it] * (1.0 - P1[it])) if P1[it] * (1.0 - P1[it]) > 0.0 else 0.0
crit = -1.0 * l1 + 2 * l2
if crit > max_crit:
max_crit = crit
threshold = it
return threshold
def GrayThreshold(image, maxval=255):
g = GrayHist(image)
thresh = GetYenThreshold(g)
threshImage_out = image.copy()
# 大于阈值的都设置为maxval
threshImage_out[threshImage_out > thresh] = maxval
# 小于阈值的都设置为0
threshImage_out[threshImage_out <= thresh] = 0
return thresh, threshImage_out
if __name__ == "__main__":
img = cv2.imread('bird.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, img_new = GrayThreshold(img_gray)
th1, img_new_1 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE)
print(th, th1)
plt.subplot(131), plt.imshow(img_gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_new, cmap='gray')
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_new_1, cmap='gray')
plt.title('CV2 Image1'), plt.xticks([]), plt.yticks([])
plt.show()