十三种基于直方图的图像全局二值化算法原理、实现、代码及效果
十三种基于直方图的图像全局二值化算法实现
- 1. 什么是基于直方图的图像全局二值化算法
- 2. 灰度平均值
- 3. 百分比阈值(P-Tile法)
- 3. 基于双峰的阈值
- 3.1 基于平均值的阈值
- 3.2 基于最小值的阈值方法
- 4. 迭代最佳阈值
- 5.大津(OTSU)法
- 6.一维最大熵
- 7.力矩保持法
- 8.模糊集
- 9. Kittler最小错误分类法
- 10. ISODATA
- 11. Shanbhag 法
- 12. Yen法
1. 什么是基于直方图的图像全局二值化算法
本文的内容来自与十三种基于直方图的图像全局二值化算法原理、实现、代码及效果。本文的中的算法是基于图像的灰度直方图进行阈值分割。
所谓图像的灰度直方图是在将图像进行灰度处理后,计算灰度值的分布。也就是每个灰度值在灰度图像中有多少个点。一般情况下,图像灰度值的取值范围为0~255,所以灰度直方图是一个有256个元素的数组。
阈值分割就是找到一个合适阈值,对灰度图像进行处理。大于阈值的设置为255,小于阈值的设置为0。也可以根据需要设为其他数值。这样处理灰度图像,可以将前景与背景做一个区分。
此方法的关键就是计算阈值。在前面的链接中,介绍了13种方法并提供了C语言实现。本文将这些算法用Python进行了重写。
2. 灰度平均值
灰度平均值是图像总像素值/总像素数:
总像素值=∑g=0255g×h(g)总像素值=\displaystyle \sum_{g=0}^{255}g\times h(g)总像素值=g=0∑255g×h(g)
总像素数=∑g=0255h(g)总像素数=\displaystyle \sum_{g=0}^{255}h(g)总像素数=g=0∑255h(g)
总像素数其实就是图像的宽度X图像的长度,也是图像的大小。
# coding:utf8import numpy as np
import cv2
from matplotlib import pyplot as pltdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef 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()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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()
对于小鸟的图像其灰度平均阈值为146。
3. 百分比阈值(P-Tile法)
此方法先设定一个阈值p(先验概率),比如50%。然后从0开始对灰度值求和。计算到灰度值Y时,[0~Y]的综合>=总像素值*p,则设定灰度值Y为阈值。
该方法需要根据先验概率确定一个百分比。人的经验很重要。
# coding:utf8import numpy as np
import cv2
from matplotlib import pyplot as pltdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef GetPTileThreshold(H,ptile=0.5):Amount = np.sum(H)A = Amount * ptilepsum = 0for Y in range(256):psum += H[Y]if psum >= A:return Yreturn -1 # 没有符合条件的阈值def GrayThreshold(image, maxval=255):g = GrayHist(image)thresh = GetPTileThreshold(g,0.25)threshImage_out = image.copy()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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. 基于双峰的阈值
此方法适合与有明显双峰值的图像,对于单峰值或者平坦的灰度直方图形并不适合。
其原理是通过迭代对直方图数据进行判断,判端是否是一个双峰的直方图。如果不是,则对直方图数据进行窗口为3的平滑。如果迭代了一定的数量比如1000次后仍未获得一个双峰的直方图,则函数执行失败。如成功获得,则最终阈值取两个双峰之间的谷底值作为阈值。
所谓窗口为3的平滑,就是将灰度p的值,用p-1,p和p+1的值进行平均。
直方图通过迭代形成双峰后,有2种方法获得阈值:
(1)双峰的平均值
(2)双峰之间的谷底值
第一种方法是找到双峰后,取平均值的整数。第二种方法找到谷底的最小值。
3.1 基于平均值的阈值
# coding:utf8
import numpy as np
import cv2
from matplotlib import pyplot as pltdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef GetIntermodesThreshold(H,method=0):Iter = 0HistGramC = 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 += 1if Iter >= 1000:return -1 # 直方图无法平滑为双峰的,返回错误代码if method > 0:Peakfound = Falsefor Y in range(1,255):if HistGramCC[Y - 1] < HistGramCC[Y] and HistGramCC[Y + 1] < HistGramCC[Y]:Peakfound = Trueif Peakfound and HistGramCC[Y - 1] >= HistGramCC[Y] and HistGramCC[Y + 1] >= HistGramCC[Y]:return Y - 1return -1else:# 阈值为两峰值的平均值Peak = np.zeros(2,dtype=np.uint16)Index = 0for Y in range(1, 255):if HistGramCC[Y - 1] < HistGramCC[Y] and HistGramCC[Y + 1] < HistGramCC[Y]:Peak[Index] = Y - 1Index += 1return int((Peak[0] + Peak[1]) / 2)def IsDimodal(H): # 检测直方图是否为双峰的# 对直方图的峰进行计数,只有峰数位2才为双峰Count = 0for Y in range(1, 255):if H[Y - 1] < H[Y] and H[Y + 1] < H[Y]:Count += 1if Count > 2: return Falseif Count == 2:return Trueelse:return Falsedef GrayThreshold(image, maxval=255):g = GrayHist(img_gray)thresh = GetIntermodesThreshold(g,1)threshImage_out = image.copy()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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。原始直方图和平滑后的直方图(黄色)如图:
3.2 基于最小值的阈值方法
该方法是查找双峰之间的最小值。使用此方法需要调用GetIntermodesThreshold(g,1)。最后一个参数设置为1即可。
4. 迭代最佳阈值
此方法是先从左边找到HistGram中第一个数值不为0的点min,再从右边找到第一个数值不为0的点max。然后取阈值threshold为此2点的中间值。通过计算[min,threshold]和(threshold,max]平均值灰度值对应的点,进行迭代直到平均灰度值点与阈值点相等。
平均灰度值计算为:
Gmin=∑g=minTkg×h(g)∑g=minTkh(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)
Gmax=∑g=Tkmaxg×h(g)∑g=Tkmaxh(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)
Tk+1=Gmin+Gmax2T_{k+1}=\frac{G_{min}+G_{max}}{2}Tk+1=2Gmin+Gmax
重复进行运算,直到Tk=Tk+1。
需要注意的是可能存在不收敛的情况。
# coding:utf8import numpy as np
import cv2
from matplotlib import pyplot as pltdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef GetIterativeBestThreshold(HistGram):Iter = 0value = np.where(HistGram > 0)[0] # 非0下标index = len(value)if index == 0:return 0elif index == 1 or index == 2:return value[0]else:MinValue = value[0]MaxValue = value[-1]Threshold = MinValueNewThreshold = int(MaxValue + MinValue) >> 1while 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 / SumTwoNewThreshold = int(MeanValueOne + MeanValueTwo) >> 1 # 求出新的阈值Iter += 1if Iter >= 1000:return -1return Thresholddef GrayThreshold(image, maxval=255):g = GrayHist(image)thresh = GetIterativeBestThreshold(g)threshImage_out = image.copy()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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。
5.大津(OTSU)法
大津法是日本学者大津在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:utf8import numpy as np
import cv2
from matplotlib import pyplot as pltdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef GetOSTUThreshold(H):# 判断只有1种灰度、2种灰度以及多种灰度V = np.where(H > 0)[0] # 非0下标I = len(V)if I == 0: return 0if 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 = 0PixelIntegralBack = 0Threshold = 0Amount = np.sum(H)PixelIntegral = np.sum(H * np.arange(256))SigmaB = -1for Y in range(MinValue, MaxValue):PixelBack = PixelBack + H[Y]PixelFore = Amount - PixelBackOmegaBack = PixelBack / AmountOmegaFore = PixelFore / AmountPixelIntegralBack += H[Y] * YPixelIntegralFore = PixelIntegral - PixelIntegralBackMicroBack = PixelIntegralBack / PixelBackMicroFore = PixelIntegralFore / PixelForeSigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore)if Sigma > SigmaB:SigmaB = SigmaThreshold = Yreturn Thresholddef GrayThreshold(image, maxval=255):g = GrayHist(image)thresh = GetOSTUThreshold(g)threshImage_out = image.copy()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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。
6.一维最大熵
最大熵的思想是将图像分为两部分(可以认为是前景和背景),循环计算两部分的灰度值的熵,使其和最大。取得最大值的灰度即为阈值。
其中熵的概念为:
hist=h(g)∑x=0255h(x)+最小正浮点值hist=\frac{h(g)}{\sum_{x=0}^{255}h(x)}+最小正浮点值hist=∑x=0255h(x)h(g)+最小正浮点值
S1=∑g=0Thist(g)S_1=\sum_{g=0}^{T}hist(g)S1=∑g=0Thist(g)
E1=−∑g=0Thist(g)S1×log(∑g=0Thist(g)S1)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))
E2=−∑g=T+1255hist(g)1−S1×log(∑g=T255hist(g)1−S1)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:utf8import numpy as np
import cv2
from matplotlib import pyplot as plt
import mathdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef Get1DMaxEntropyThreshold(H):V = np.where(H > 0)[0] # 非0下标I = len(V)if I == 0: return 0if 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=MinValueHistGramD = np.zeros(256,dtype=np.float64)Amount = np.sum(H[MinValue:MaxValue + 1])eps = np.finfo(np.float64).epsHistGramD[MinValue:MaxValue+1] = H[MinValue:MaxValue+1]/Amount + epsMaxEntropy = 0.0for 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 = YMaxEntropy = EntropyBack + EntropyForereturn Thresholddef GrayThreshold(image,maxval=255):g = GrayHist(image)thresh = Get1DMaxEntropyThreshold(g)threshImage_out = image.copy()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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。
7.力矩保持法
力矩法的论文可以从此链接中获得力矩法论文。上述论文是英文版的。此方法比较复杂。
实现方法如下:
# coding:utf8import numpy as np
import cv2
from matplotlib import pyplot as plt
import mathdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef 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 0if 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) / AmountIndex = 0B = 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 = Yreturn int(Index)def GrayThreshold(image, maxval=255):g = GrayHist(image)thresh = GetMomentPreservingThreshold(g)threshImage_out = image.copy()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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()
8.模糊集
具体方法参考基于模糊集理论的一种图像二值化算法的原理、实现效果及代码
# coding:utf8import numpy as np
import cv2
from matplotlib import pyplot as plt
import mathdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef GetHuangFuzzyThreshold(H):Threshold = -1BestEntropy = np.finfo(np.float32).max# 找到第一个和最后一个非0的色阶值V = np.where(H > 0)[0] # 非0下标I = len(V)if I == 0: return 0if 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 1for 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 = 0mu = int(np.round(float(W[Y] / S[Y]))) # 公式17for 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 0for X in range(Y + 1,Last+1):Entropy += Smu[abs(X - mu)] * H[X] # 公式8if BestEntropy > Entropy:BestEntropy = Entropy #取最小熵处为最佳阈值Threshold = Yreturn Thresholddef GrayThreshold(image, maxval=255):g = GrayHist(image)thresh = GetHuangFuzzyThreshold(g)threshImage_out = image.copy()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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()
9. Kittler最小错误分类法
具体方法见Kittler, J & Illingworth, J (1986), “Minimum error thresholding”, Pattern Recognition 19: 41-47
# coding:utf8import numpy as np
import cv2
from matplotlib import pyplot as plt
import mathdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef GetKittlerMinError(HistGram):value = np.where(HistGram>0)[0] #非0下标index = len(value)if index == 0:return 0elif index == 1:return value[0]elif index == 2:return value[0]else:MinValue = value[0]MaxValue = value[-1]Threshold = -1MinSigma = np.finfo(np.float64).epsfor Y in range(MinValue,MaxValue):PixelBack = 0PixelFore = 0OmegaBack = 0OmegaFore = 0for 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 / PixelBackOmegaFore = OmegaFore / PixelForeSigmaBack = 0SigmaFore = 0for 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 = Yelse: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 = SigmaThreshold = Yreturn Thresholddef GrayThreshold(image,maxval=255):g = GrayHist(image)thresh = GetKittlerMinError(g)threshImage_out = image.copy()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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()
10. ISODATA
参考论文:
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:utf8import numpy as np
import cv2
from matplotlib import pyplot as plt
import mathdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef GetIsoDataThreshold(H):V = np.where(H > 0)[0] # 非0下标I = len(V)if I == 0: return 0if 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 = MinValuewhile (True):SumOne = 0SumInteralOne = 0SumTwo = 0SumInteralTwo = 0for i in range(threshold):SumOne += H[i]SumInteralOne += H[i] * ifor i in range(threshold+1,MaxValue):SumTwo += H[i]SumInteralTwo += (H[i] * i)if SumOne > 0 and SumTwo > 0:SumInteralOne /= SumOneSumInteralTwo /= SumTwoif threshold == int(np.round((SumInteralOne + SumInteralTwo) / 2.0)):breakthreshold += 1if threshold > 253:return 0return thresholddef GrayThreshold(image,maxval=255):g = GrayHist(image)thresh = GetIsoDataThreshold(g)threshImage_out = image.copy()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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()
11. Shanbhag 法
参考论文:
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:utf8import numpy as np
import cv2
from matplotlib import pyplot as plt
import mathdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef GetShanbhagThreshold(H):V = np.where(H > 0)[0] # 非0下标I = len(V)if I == 0: return 0if 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 / AmountP1[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 = 0for ih in range(1, 256):if not (math.fabs(P1[ih]) < np.finfo(np.float64).eps):first_bin = ihbreaklast_bin = 255for ih in range(255, first_bin - 1, -1):if not (math.fabs(P2[ih]) < np.finfo(np.float64).eps):last_bin = ihbreakthreshold = -1min_ent = np.finfo(np.float64).maxfor it in range(first_bin, last_bin + 1):ent_back = 0.0term = 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 *= terment_obj = 0.0term = 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 *= termtot_ent = math.fabs(ent_back - ent_obj)if tot_ent < min_ent:min_ent = tot_entthreshold = itreturn thresholddef GrayThreshold(image, maxval=255):g = GrayHist(image)thresh = GetShanbhagThreshold(g)threshImage_out = image.copy()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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()
12. Yen法
参考论文:
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-3782) Sezgin M. and Sankur B. (2004) "Survey over Image Thresholding Techniques and Quantitative Performance Evaluation" Journal of Electronic Imaging, 13(1): 146-165
# coding:utf8import numpy as np
import cv2
from matplotlib import pyplot as plt
import mathdef GrayHist(img):grayHist = np.zeros(256, dtype=np.uint64)for v in range(256):grayHist[v] = np.sum(img == v)return grayHistdef GetYenThreshold(H):V = np.where(H > 0)[0] # 非0下标I = len(V)if I == 0: return 0if 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.0for ih in range(254, -1, -1):P2_sq[ih] = P2_sq[ih + 1] + norm_histo[ih + 1] * norm_histo[ih + 1]threshold = -1max_crit = np.finfo(np.float64).epsfor it in range(256):l1 = math.log(P1_sq[it] * P2_sq[it]) if P1_sq[it] * P2_sq[it] > 0.0 else 0.0l2 = math.log(P1[it] * (1.0 - P1[it])) if P1[it] * (1.0 - P1[it]) > 0.0 else 0.0crit = -1.0 * l1 + 2 * l2if crit > max_crit:max_crit = critthreshold = itreturn thresholddef GrayThreshold(image, maxval=255):g = GrayHist(image)thresh = GetYenThreshold(g)threshImage_out = image.copy()# 大于阈值的都设置为maxvalthreshImage_out[threshImage_out > thresh] = maxval# 小于阈值的都设置为0threshImage_out[threshImage_out <= thresh] = 0return thresh, threshImage_outif __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.描述: 此方法实用于具有明显双峰直方图的图像,其寻找双峰的谷底作为阈值,但是该方法不一定能获得阈值,对于那些具有平坦的直方图或单峰图像,该方法不合适. 2.实现过程: 该函数的实现是一个迭代的过程 ...
- 场景文本检测算法 可微分二值化DBNet原理与代码解析
目录 原理介绍 Label Generation Loss函数 后处理 论文 https://arxiv.org/abs/1911.08947 代码 https://github.com/WenmuZ ...
- matlab最小分类错误全局二值化算法
转自:http://download.csdn.net/detail/hupeng810/1511870 function imagBW = kittlerMet(imag) % KITTLERMET ...
- matlab确定灰度阈值T,matlab灰度图像二值化【灰度图像二值化算法研究】
摘要: 在很多图像处理的过程中,经常需要对灰度图像进行二值化.本文对几种常用的图像二值化算法进行了阐述,并通过仿真,进行比较研究.根据实验结果,阐明了各种算法的优缺点. Abstract: The b ...
- 二值化算法OTSU源码解析
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 概述: 本文中小编将会跟大家分享一下OpenCV3.1.0中图像二 ...
- 【图像处理】——图像的二值化操作及阈值化操作(固定阈值法(全局阈值法——大津法OTSU和三角法TRIANGLE)和自适应阈值法(局部阈值法——均值和高斯法))
目录 一.二值化的概念(实际上就是一个阈值化操作) 1.概念: 2.实现方法 3.常用方法 二.阈值类型 1.常见阈值类型(主要有五种类型) (1)公式描述 (2)图表描述 2.两种特殊的阈值算法(O ...
- python-opencv 图像阈值二值化
本文讲解基于OpenCV-python的图像二值化API及浅显原理讲解 文章目录 一. 阈值 1. 简单阈值 2. 自适应阈值 二. 图像二值化 1. 全局图像二值化 2. 局部图像二值化 3. Ot ...
最新文章
- EEMD算法的基本原理
- Centos中Redis的下载编译与安装(超详细)
- python类百度百科_Python抓取百度百科数据
- C语言学习之插入排序
- linux x64下安装oracle 11g
- [原创]完美开启Win8中管理员Administrator帐户
- (41)zabbix监控api接口性能及可用性 天气预报api为例
- html文件form根目录,HTML ,form 和 link 使用根目录 的问题,我已经上图了!
- mysqli 语句和mysql语句一样吗_mysqli语句的用法
- 设计开发-API代付系统/自动代付系统
- 标准差、均方误差、均方根误差、平均绝对误差
- Word中批量进行中英文标点的转换
- B题Tomb Raider ---- 一 。启动emule客户端
- win10计算机入门使用教程,win10系统使用教程_windows10基本使用教程图文步骤
- ipq wifi校准
- 看天下网络资讯浏览器 下载
- Redis原子计数器incr,防止并发请求
- NOIP2016·洛谷·天天爱跑步
- python中if条件语句的代码实例
- 高校教材管理系统mysql_高校教材管理系统的设计与实现