9. OpenCV--图像二值化(Binary Image)

》》点赞,收藏+关注,理财&技术不迷路《《

目录:

9.1 二值化概念与原理

二值化  ——  图像只有两种色,黑白。0-1,0=黑,1=白

灰度:0-255

彩色:255,255,255

定义:图像的二值化,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。

           一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体,常用的方法就是设定一个阈值T,用T将图像的数据分成两部分:大于T的像素群和小于T的像素群。这是研究灰度变换的最特殊的方法,称为图像的二值化(Binarization)。

其实图像二值化的方法有不下二十种。一般分为Global和Local两类,区别就是寻找那个阈值的时候使用到了哪里的信息。

一般是为了将感兴趣目标和背景分离。

图像的二值化是最简单的图像处理技术,它一般都跟具体算法联系在一起,很多算法的输入需要是二值数据。比如你把图像文字转换为PDF文字,PDF上只能是黑白两种颜色。比如你给二维码解码,你需要知道哪块黑哪块白。

去掉图像两个字,二值化在视觉里面应用场景多一些。比如早期的人脸检测很多手势识别的方法,第一步要找到皮肤块,所以需要把图像分为皮肤区域和非皮肤区域,这也算是一种二值化,但通常在方法上用的可能不是基于直方图的了(可能是ID3, 随机森林,SVM,甚至神经网络)。比如Haar特征,以及后来的BRIEF和FAST等,还有LBP实际上都是用到二值的思想。

图像二值化可以看作是聚类,可以看作是分类……这些其实不重要,重要的是它快。它最明显的意义就是简化后期的处理,提高处理的速度。`

但是单一的图像二值化方法(指基于直方图的二值化)往往比不过其他的方法,因为,毕竟你信息丢了太多。但是二值化快啊……你可以进行一百次不同的二值化,然后再得到一个更好的结果……

上面右图中下面两个图是还可以对二值图像进行编码。编码后就更好压缩了,那么我们就可以高度节省内存。

图像二值化方法:

全局阈值

局部阈值


9.2 OpenCV中图像二值化方法:

评判某个算法是否好,就看二值化图像信息是否丢失了很多。

threshold(gray_src, dst, threshold_value, threshold_max,THRESH_BINARY); 

//原图,目标图,已知阈值,阈值最大值,阈值类型

返回阈值的值和图像。

阈值:简单点说是把图像分割的标尺。这个标尺是根据什么产生的,可以用阈值产生算法(opencv有两个算法)或者是自己指定一个阈值来进行分割。

如下分苹果,大于某一像素的变为黑色,小于某一像素的变为白色,即可通过阈值把图像分割了。

可以根据有5种阈值类型来分割(Binary segmentation二值分割)。

printf("%d", THRESH_BINARY); //0,二值化

printf("%d", THRESH_BINARY_INV); //1,反二值化

printf("%d", THRESH_TRUNC); //2,截断

printf("%d", THRESH_TOZERO); //3,取零

printf("%d\n", THRESH_TOZERO_INV); //4,反取零


9.2.1 5种阈值处理类型(手动)

9.2.1.1 阈值二值化(threshold binary)

THRESH_BINARY

threshold(gray_src, dst, threshold_value, threshold_max, THRESH_BINARY); 

//阈值二值化

左下方直方图表示图像像素点Src(x,y)值的分布情况,蓝色水平线表示阈值,大于阈值的取最大值255,小于阈值的取最小值0:


9.2.1.2 阈值反二值化(threshold binary Inverted)

THRESH_BINARY_INV

左下方的图表示图像像素点Src(x,y)值分布情况,蓝色水平线表示阈值,大于阈值的取最小值0,小于阈值的取最大值255:


9.2.1.3 截断 (truncate)

THRESH_TRUNC

左下方的图表示图像像素点Src(x,y)值分布情况,蓝色水平线表示阈值 ,大于阈值的跟阈值相等,小于阈值的不变:


9.2.1.4 阈值取零 (threshold to zero)

THRESH_TOZERO

左下方的图表示图像像素点Src(x,y)值分布情况,蓝色水平线表示阈值,大于阈值的不变,小于阈值的取最小值0:


9.2.1.5 阈值反取零 (threshold to zero inverted)

THRESH_TOZERO_INV

左下方的图表示图像像素点Src(x,y)值分布情况,蓝色水平线表示阈值,大于阈值的取最小值0,小于阈值的不变:


9.2.2 2种阈值寻找方法(自动/全局; 基于直方图)

threshold(gray_src, dst, 0, 255, THRESH_OTSU | type_value); 

//自动计算二值化otsu阈值,忽略输入的阈值

9.2.2.1 Triangle三角阈值法(基于直方图)

该方法是使用直方图数据,基于纯几何方法来寻找最佳阈值,它的成立条件是假设直方图最大波峰在靠近最亮的一侧,然后通过三角形求得最大直线距离,根据最大直线距离对应的直方图灰度等级即为分割阈值,图示如下:

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

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

所以triangle 非常适合用在只有单个波峰时候!!!!多个波峰的话,应用起来会很差。所以做医学图像处理会很好,因为细胞图片的直方图最多都只有三个波峰,这个方法是从生物医学衍生出的。

算法步骤

1. 图像转灰度

2. 计算图像灰度直方图

3. 寻找直方图中两侧边界

4. 寻找直方图最大值

5. 检测是否最大波峰在亮的一侧,否则翻转

6. 计算阈值得到阈值T,如果翻转则255-T

左边为原图,通过直方图来看像素值分布,通过阈值保留,我们看右图发现前景被保留下来了所以为白色,背景就为黑色了。好的二值化方法就应该如右图一样。

二值化方法中的三角二值化:通过直方图来对图像进行三角二值化,连接波峰波谷然后做垂直线。


9.2.2.2 OTSU阈值法(基于直方图)

直方图统计学

otsu 大津算法介绍:

OTSU算法是由日本学者OTSU于1979年提出的一种对图像进行二值化的高效算法。

利用阈值将原图像分成前景,背景两个图象。

前景:用n1,csum,m1来表示在当前阈值下的前景的点数,质量矩,平均灰度

背景:用n2, sum-csum,m2来表示在当前阈值下的背景的点数,质量矩,平均灰度

当取最佳阈值时,背景应该与前景差别最大,关键在于如何选择衡量差别的标准,而在otsu算法中这个衡量差别的标准就是最大类间方差,在本程序中类间方差用sb表示,最大类间方差用fmax

这段引用自百度百科,不是很好懂。

otsu 大津算法原理

otsu 大津算法是一种图像二值化算法,作用是确定将图像分成黑白两个部分的阈值。

将图像背景和前景分成黑白两类很好理解,但是如何确定背景和前景的二值化界限(阈值)呢?

对于不同的图像,这个阈值可能不同,这就需要有一种算法来根据图像的信息自适应地确定这个阈值。

首先,需要将图像转换成灰度图像,255个灰度等级。

可以将图像理解成255个图层,每一层分布了不同的像素,这些像素垂直叠加合成了一张完整的灰度图。

我们的目的就是找到一个合适的灰度值,大于这个值的我们将它称之为背景(灰度值越大像素越黑),小于这个值的我们将它称之为前景(灰度值越小像素越白)。

怎么确定这个值就是我们想要的值呢?

这里引入方差的概念,方差越大,相关性越低,黑白越分明。

我们将每一个灰度值之上下之间的像素的方差求出来不就行了吗?找到方差最大的那个灰度值,那个就是我们想要的二值化分隔阈值。

先定义几个符号代表的意义:

h:图像的宽度

w:图像的高度(h*w 得到图像的像素数量)

t :灰度阈值(我们要求的值,大于这个值的像素我们将它的灰度设置为255,小于的设置为0)

n0:小于阈值的像素,前景

n1:大于等于阈值的像素,背景

n0 + n1 == h * w

w0:前景像素数量占总像素数量的比例

w0 = n0 / (h * w)

w1:背景像素数量占总像素数量的比例

w1 = n1 / (h * w)

w0 + w1 == 1

u0:前景平均灰度

u0 = n0灰度累加和 / n0

u1:背景平均灰度

u1 = n1灰度累加和 / n1

u:平均灰度

u = (n0灰度累加和 + n1灰度累加和) / (h * w) 根据上面的关系

u = w0 * u0 + w1 * u1

g:类间方差(那个灰度的g最大,哪个灰度就是需要的阈值t)

g = w0 * (u0 - u)^2 + w1 * (u1 - u)^2

根据上面的关系,可以推出:(这个一步一步推导就可以得到)

g = w0 * w1 * (u0 - u1) ^ 2

然后,遍历每一个灰度值,找到这个灰度值对应的 g

找到最大的 g 对应的 t


9.2.3 自适应阈值(局部阈值)

可以想象为把矩阵分成模块化,然后各个模块求平均和阈值比较。(所以又有两种平均方法,还有模块大小选择,比较方法是直接单独比较?还是相差在某个范围内?后者好,因为可以考虑到误差,误差 = 像素值中混杂了噪音)

这个方法只返回binary图像,不返回set阈值的值。

C++: void adaptiveThreshold(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)

InputArray src:源图像

OutputArray dst:输出图像,与源图像大小一致

int adaptiveMethod:在一个邻域内计算阈值所采用的算法,有两个取值,分别为ADAPTIVE_THRESH_MEAN_C 和 ADAPTIVE_THRESH_GAUSSIAN_C 。

ADAPTIVE_THRESH_MEAN_C的计算方法是计算出领域的平均值再减去第七个参数double C的值

ADAPTIVE_THRESH_GAUSSIAN_C的计算方法是计算出领域的高斯均值再减去第七个参数double C的值

int thresholdType:这是阈值类型,只有两个取值,分别为 THRESH_BINARY 和THRESH_BINARY_INV  具体的请看官方的说明,这里不多做解释

int blockSize:adaptiveThreshold的计算单位是像素的邻域块,邻域块取多大,就由这个值作决定

double C:在对参数int adaptiveMethod的说明中,我已经说了这个参数的作用,从中可以看出,这个参数实际上是一个偏移值调整量

从上面的说明中可以看出,使用函数adaptiveThreshold的关键是确定blockSize和C的值,明白了这两个值的意义之后,在实际项目中,应该可以根据试验法选出较为合适的值吧!

我们改为高斯阈值:


9.2.4 自定义分割

这里我们定义全局平均数来作为阈值分割图像。


上面这一大步是为了将数组矩阵拉直然后求出平均数。一定要先转为 灰度图,因为rgb有三个通道。


9.3 超大图像二值化

# 将大图片拆分成小图片后再用自适应局部阈值比较好

# 因为超大图像一般都是几千万或者几亿像素。1000*1000像素也才100万像素。

# 对于超大像素而言,一个窗口根本显示不了那么多像素。要么就压缩才能显示。


9.3.1 全局阈值(无法排除噪声影响)

本来应该是白色的地方,处理后灰度图中出现了雪花格子,这是噪声引起的,因为我们的OTSU有这个缺点。所以全局阈值有缺点。


9.3.2 全局阈值优化(手动过滤)

全局阈值的上述问题可以通过下面这个方法解决,也可以通过局部阈值解决。以下方法就是把方差小于 某个数 然后直接覆盖为255或0. 这样就排除了噪声干扰(当然也有可能影响到一点点原图像)但肯定比原来要好很多。


9.3.3 局部阈值(可以解决噪声问题)

局部阈值方法中我们考虑了噪声的影响,下面方法我们设立的噪声范围在+-20内,但是全局阈值就没有考虑噪声,所以直接二值化会出现很多错误。

我们还输出了均值和方差,方差为0,均值都为255,说明这一块是空白图像。我们也可以当方差或者mean小于某个数时,直接把它给赋值为0. 这样就可以消除一些噪声产生的误差。

你可能感兴趣的:(9. OpenCV--图像二值化(Binary Image))