算法核心:找阈值!!!
参考文章:二全局阈值使用thresh_triangle三角形算法
(重要)
一. 局部自适应二值化采用了分块,效果比全局二值化只采用了一种threshold这种,效果要好很多
二. 全局二值化算法:
1.OTSU算法(大津法或最大类间方差法)是由日本学者OTSU于1979年提出的一种对图像进行二值化的高效算法
① Otsu最大类间方差法原理
利用阈值将原图像分成前景,背景两个图象。
前景:用n1,csum,m1来表示在当前阈值下的前景的点数,质量矩,平均灰度
后景:用n2, sum-csum,m2来表示在当前阈值下的背景的点数,质量矩,平均灰度
当取最佳阈值时,背景应该与前景差别最大,关键在于如何选择衡量差别的标准,而在otsu算法中这个衡量差别的标准就是最大类间方差,在本程序中类间方差用sb表示,最大类间方差用fmax
②关于最大类间方差法(otsu)的性能:
类间方差法对噪音和目标大小十分敏感,它仅对类间方差为单峰的图像产生较好的分割效果。
当目标与背景的大小比例悬殊时,类间方差准则函数可能呈现双峰或多峰,此时效果不好,但是类间方差法是用时最少的。
③最大类间方差法(otsu)的公式推导:
记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)*(u0-u1),此公式为方差公式。
可参照概率论课本上面的g的公式也就是下面程序中的sb的表达式。当方差g最大时,可以认为此时前景和背景差异最大,此时的灰度t是最佳阈值sb = w0*w1*(u1-u0)*(u0-u1)
2.三角形法
①三角法求阈值最早见于Zack的论文《Automatic measurement of sister chromatid exchange frequency》,主要是用于染色体的研究,该方法是使用直方图数据,基于纯几何方法来寻找最佳阈值,它的成立条件是假设直方图最大波峰在靠近最亮的一侧,然后通过三角形求得最大直线距离,根据最大直线距离对应的直方图灰度等级即为分割阈值,图示如下:
对上图的详细解释:
在直方图上从最高峰处bmx到最暗对应直方图bmin(p=0)%构造一条直线,从bmin处开始计算每个对应的直方图b到直线的垂直距离,直到bmax为止,其中最大距离对应的直方图位置即为图像二值化对应的阈值T。
扩展情况:
有时候最大波峰对应位置不在直方图最亮一侧,而在暗的一侧,这样就需要翻转直方图,翻转之后求得值,用255减去即得到为阈值T。扩展情况的直方图表示如下:
②算法步骤
1. 图像转灰度
2. 计算图像灰度直方图
3. 寻找直方图中两侧边界
4. 寻找直方图最大值
5. 检测是否最大波峰在亮的一侧,否则翻转
6. 计算阈值得到阈值T,如果翻转则255-T
3. 高斯混合分布的全局阈值算法
本程序的原理很简单:如下图所示,蓝色的代表一个灰度图片的直方图,想办法找到一个合适的混合高斯分布去逼近它。混合高斯分布,其实就是两个高斯分布之和。我们认为两个高斯分布相交的地方的灰度值,就是要选择的阈值。
那么怎么去寻找一个逼近它的高斯分布呢?我用的是一个比较笨的方法,取一个变量t,将这个变量t,从左到右遍历每一个灰度。每次以t左边的数据当成一组样本来估计一个高斯分布,以t右边的数据为另一个样本来估计第二个高斯分布。将这两个高斯分布组合成的混合高斯分布,跟原始的数据匹配。至于匹配标准可以随意选择,可以用最小均方误差,也可以用最小绝对值误差,也可以用相关程度。随着t的遍历,找到每一次混合高斯分布与原始数据的匹配程度,选择最为匹配的混合高斯分布所对应的t值值为阈值。
实践证明,这种方法的自适应性还是很好的,鲁棒性比较好。
当改变直方图时,混合高斯模型的匹配效果,还是很多不错的。
但是有一点,就是要求处理的图片,灰度不能太复杂,尽量符合混合高斯模型。
三. 局部自适应二值化:
1.平均值算法
2.高斯自适应法 参考:
二值化原理:
把一个灰度图像二值化,其实就是找到一个阈值,使这个较低中,灰度大于这个阈值的,设置成255,灰度小于这个阈值的,设置为0。
阈值自适应二值化:
非自适应的二值化呢,有一个问题,就是一个阈值往往只对应一类图像,如果图像的光照变暗了,那个单阈值情况的二值化效果会大大的折扣。
自适应二值化其实就是一种根据图片的灰度直方图,得到一个适合本图像的二值化阈值。
代码如下:
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
def global_threshold_demo(image):
'''二值化'''
#1.灰度化
gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
#2.计算直方图
hist=cv.calcHist([gray],[0],None,[256],[0,256])
plt.plot(hist)
plt.xlim([0, 256])
plt.show()
#二值化,ret代表阈值,binary是二值图像
#cv.THRESH_OTSU,cv.THRESH_TRIANGLE 计算阈值的方法,加上竖线,自动寻找阈值
# ret,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY | cv.THRESH_OTSU)#ret:阈值,binary:二值图像
# ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE) # ret:阈值,binary:二值图像
# ret, binary = cv.threshold(gray, 100, 255, cv.THRESH_BINARY) # 手工指定阈值:100,去掉自动寻找阈值
# ret, binary = cv.threshold(gray, 100, 255, cv.THRESH_BINARY_INV) #100,取反
# ret, binary = cv.threshold(gray, 100, 255, cv.THRESH_TRUNC) #100,取截断,>100的全部变成100
ret, binary = cv.threshold(gray, 100, 255, cv.THRESH_TOZERO) #100,<100的全部变成0
print('ret:',ret)
# cv.imshow('gray',gray)
cv.imshow('binary', binary)
def local_threshold_demo(image):
'''局部自适应二值化'''
gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
#pram:[gray,max_value,local_binary_method,binary_method,blocksize:奇数必须,value-mean>10?white:black]
# dst=cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,25,10)#means_c方法,采用对区域内的像素进行均值加权计算
dst=cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,25,10)#GAUSSIAN_C方法,采用高斯,区域中的(x,y)周围的像素根据高斯函数按照它们离中心点的距离进行加权计算
cv.imshow('local_binary',dst)
src=cv.imread('D:/opencv/meinv.jpg')
cv.imshow('src',src)
# global_threshold_demo(src)#全局二值化
local_threshold_demo(src)#局部二值化
cv.waitKey(0)
cv.destroyAllWindows()