上一节中我们讲解了什么是二值化,并且讲到了二值化的一般方法,那么每种算法究竟是怎么样对图像经行二值化处理的呢?,算法的原理是什么呢,怎么样用代码实现,这节我们分享下。
1.otsu(最大类间方差法、大津法)
最大类间方差法是由日本学者大津于1979年提出的,是一种自适应的阈值确定的方法,又叫大津法,简称OTSU。它是按图像的灰度特性,将图像分成背景和目标2部分。背景和目标之间的类间方差越大,说明构成图像的2部分的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致2部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。
阈值将原图象分成前景,背景两个图象。
当取最佳阈值时,背景应该与前景差别最大,关键在于如何选择衡量差别的标准
而在otsu算法中这个衡量差别的标准就是最大类间方差(英文简称otsu,这也就是这个算法名字的来源)
对于图像I(x,y),前景(即目标)和背景的分割阈值记作T,前景图像占整幅图像的比例记为ω0,其平均灰度μ0;背景图像占整幅图像的比例为ω1,其平均灰度为μ1。图像的总平均灰度记为μ,类间方差记为g。M×N = 像素总数,图像中像素的灰度值小于阈值T的像素个数记作N0,像素灰度大于阈值T的像素个数记作N1
,则有:
前景图像占比 ω0=N0/ M×N (1)
背景图像占比 ω1=N1/ M×N (2)
前景像素+背景像素 N0+N1=M×N (3)
背景图像+前景图像占比 ω0+ω1=1 (4)
0~M灰度区间的灰度累计值 (5)
类间方差值 (6)
将式(5)代入式(6),得到等价公式:
(7)
或
采用遍历的方法得到使类间方差最大的阈值T,即为所求。
代码实现:
c代码
1 | w0为背景像素点占整幅图像的比例 |
MATALB代码:
1 | close all; |
或者直接调用MATALB函数
1 | I=imread('D:\\Images\\pic_loc\\1870405130305041503.jpg'); |
缺陷:OSTU算法在处理光照不均匀的图像的时候,效果会明显不好,因为利用的是全局像素信息。
2.灰度平局值法:
** 1、描述:即使用整幅图像的灰度平均值作为二值化的阈值,一般该方法可作为其他方法的初始猜想值。**
原理:
代码实现:
1 | public static int GetMeanThreshold(int* HistGram) |
缺点:同样受光线影响较大,但是方法简单,处理快
3.双峰法
介绍:如果图像灰度直方图呈明显的双峰状,则选取双峰间的最低谷出作为图像分割的阈值所在。,如下图,以T为阈值进行二值化分,可以将目标和背景分割开。
在一些简单的图像中,物体的灰度分布比较有规律,背景与各个目标在图像的直方图各自形成一个波峰,即区域与波峰一一对应,每两个波峰之间形成一个波谷。那么,选择双峰之间的波谷所代表的灰度值T作为阈值,即可实现两个区域的分割。如下图所示。
代码实现:
1 | int GetIntermodesThreshold(int* HistGram) |
Python代码:
1 | #coding:utf-8import cv2import numpy as npfrom matplotlib import pyplot as plt |
缺点:当不同区域(即目标)之间的灰度分布有一定的重叠时,双峰法的效果就很差,也就是说,图像为双峰时才能用双峰法
上述代码已经给出判断双峰图的代码
4最佳迭代法
迭代法图像二值化的算法思想是:首先,初始化一个阈值Th,然后按照某种策略通过迭代不断更新这一阈值,直到满足给定的约束条件为止。
迭代法是基于逼近的思想,迭代阈值的获取步骤可以归纳如下:
(1)求出图象的最大灰度值和最小灰度值,分别记为gl和gu,令初始阈值为:

(2) 根据阈值T0将图象分割为前景和背景,分别求出两者的平均灰度值Ab和Af:
(3) 令

如果Tk=Tk+1,则取Tk为所求得的阈值,否则,转2继续迭代
MATALB代码实现:
1 | >> clear all |
代码实现:
1 | public static int GetIterativeBestThreshold(int\[\] HistGram) |
5百分比阈值(P-Tile法)
p-tile算法是一种基于灰度直方图统计的的自动阈值选择算法,该算法需要基于一定的先验条件—背景与目标所占的面积比P%。
该算法选择阈值的原则是,依次累积灰度直方图,直到该累积值大于或等于前景图像(目标)所占面积,此时的灰度级即为所求的阈值
代码实现:
1 | //HistGram灰度图像的直方图 |
缺点:该方法简单高效,但是对于先验概率难于估计的图像却无能为力。。条件很苛刻,大部分情况下都用不上
6.Niblack二值化算法
Niblack二值化算法是比较简单的局部阈值方法,阈值的计算公式是T = m + k*v,其中m为以该像素点为中心的区域的平均灰度值,v是该区域的标准差,k是一个修正系数
它根据以像素点为中心的邻域内的点的情况为此像素计算阈值。下面是每个像素阈值的计算公式,m是均值,s是标准差
MATALB代码:
1 | function g=segNiBlack(f,w2,k) |
C代码:
1 | void NiBlack(BYTE \*image\_in, BYTE \*image\_out, int xsize, int ysize){/*//////////////////////////////////////////////////////////////////// |
C
1 | /// <summary> /// 快速的二维数组元素局部窗口求和程序 /// </summary> /// <param name="array"> 输入二维数组</param> /// <param name="winR">窗口半径</param> /// <returns>输出结果</returns> /// <summary> public static int\[,\] LocalSum_Fast(byte\[,\] array, int winR) |
7.bernsen二值化
bernsen算法的中心思想:
先人为设定两个值S与TT(Bemsen最初设S为15,TT设为128),计算以图像中任意像素尸为中心的大小为k×k窗口内的所有像素的最大值M与最小值N,两者的均值T,如果朋M-N大于S,则当前P的阈值为T;若小于S,则表示该窗口所在区域灰度级灰度级差别较小,那么窗口在目标区或在背景区,再判断T与TT的关系,若T>TT则当前点灰度值为255,否则当前点灰度值为0。
改进的bernsen算法:
1.消除个别灰度特异点,设采用的阈值为T1。
T1的取值满足:
A为图像的总像素个数
代码实现:
1 | int getThreshBernsen(IplImage *src) |
二值化的方法有很多,基于每个人来说都会有着适合自己的方法,这里我们只介绍上述几种主流方法,正常使用已经足以,方法不在于多,而在于精,可能你用一种方法就很完美,也可能要不断修改,找到最适合的,图像处理好才是王道。
参考:
ttps://www.cnblogs.com/Imageshop/p/3307308.html
https://blog.csdn.net/liuzhuomei0911/article/details/51440305
https://blog.csdn.net/jinzhichaoshuiping/article/details/51480520
https://www.cnblogs.com/naniJser/archive/2012/12/12/2814324.html
https://blog.csdn.net/wu\_lian\_nan/article/details/69371720
https://blog.csdn.net/zyzhangyue/article/details/45841121
还有一些参考较少的文献,这里就不罗列了,写这个用到参考文献实在太多,抱歉抱歉
整理实属不易,点个赞再走呗!