算法核心:找阈值!!!

参考文章:二全局阈值使用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》,主要是用于染色体的研究,该方法是使用直方图数据,基于纯几何方法来寻找最佳阈值,它的成立条件是假设直方图最大波峰在靠近最亮的一侧,然后通过三角形求得最大直线距离,根据最大直线距离对应的直方图灰度等级即为分割阈值,图示如下:

  

二值化demo 二值化的算法有哪些_直方图

  对上图的详细解释:
  在直方图上从最高峰处bmx到最暗对应直方图bmin(p=0)%构造一条直线,从bmin处开始计算每个对应的直方图b到直线的垂直距离,直到bmax为止,其中最大距离对应的直方图位置即为图像二值化对应的阈值T。

  扩展情况:
  有时候最大波峰对应位置不在直方图最亮一侧,而在暗的一侧,这样就需要翻转直方图,翻转之后求得值,用255减去即得到为阈值T。扩展情况的直方图表示如下:

  ②算法步骤
  1. 图像转灰度
  2. 计算图像灰度直方图
  3. 寻找直方图中两侧边界
  4. 寻找直方图最大值
  5. 检测是否最大波峰在亮的一侧,否则翻转
  6. 计算阈值得到阈值T,如果翻转则255-T

3. 高斯混合分布的全局阈值算法

  本程序的原理很简单:如下图所示,蓝色的代表一个灰度图片的直方图,想办法找到一个合适的混合高斯分布去逼近它。混合高斯分布,其实就是两个高斯分布之和。我们认为两个高斯分布相交的地方的灰度值,就是要选择的阈值。

       

二值化demo 二值化的算法有哪些_直方图_02

 

  那么怎么去寻找一个逼近它的高斯分布呢?我用的是一个比较笨的方法,取一个变量t,将这个变量t,从左到右遍历每一个灰度。每次以t左边的数据当成一组样本来估计一个高斯分布,以t右边的数据为另一个样本来估计第二个高斯分布。将这两个高斯分布组合成的混合高斯分布,跟原始的数据匹配。至于匹配标准可以随意选择,可以用最小均方误差,也可以用最小绝对值误差,也可以用相关程度。随着t的遍历,找到每一次混合高斯分布与原始数据的匹配程度,选择最为匹配的混合高斯分布所对应的t值值为阈值。

  实践证明,这种方法的自适应性还是很好的,鲁棒性比较好。

   

二值化demo 二值化的算法有哪些_二值化demo_03

  当改变直方图时,混合高斯模型的匹配效果,还是很多不错的。

  但是有一点,就是要求处理的图片,灰度不能太复杂,尽量符合混合高斯模型。

 

三. 局部自适应二值化:

 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()