c++获取图像在直方图_Android OpenCV(八):图像二值化

图像二值化

简介

图像二值化( Image Binarization)就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程。在数字图像处理中,二值图像占有非常重要的地位,图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。

原理

图像的二值化处理就是将图像上的点的灰度值为0或255,也就是将整个图像呈现出明显的黑白效果。即将256个亮度等级的灰度图像通过适当的阈值选取而获得仍然可以反映图像整体和局部特征的二值化图像。在数字图像处理中,二值图像占有非常重要的地位,特别是在实用的图像处理中,以二值图像处理实现而构成的系统是很多的,要进行二值图像的处理与分析,首先要把灰度图像二值化,得到二值化图像,这样子有利于在对图像做进一步处理时,图像的集合性质只与像素值为0或255的点的位置有关,不再涉及像素的多级值,使处理变得简单,而且数据的处理和压缩量小。为了得到理想的二值图像,一般采用封闭、连通的边界定义不交叠的区域。所有灰度大于或等于阈值的像素被判定为属于特定物体,其灰度值为255表示,否则这些像素点被排除在物体区域以外,灰度值为0,表示背景或者例外的物体区域。
如果某特定物体在内部有均匀一致的灰度值,并且其处在一个具有其他等级灰度值的均匀背景下,使用阈值法就可以得到比较的分割效果。如果物体同背景的差别表现不在灰度值上(比如纹理不同),可以将这个差别特征转换为灰度的差别,然后利用阈值选取技术来分割该图像。动态调节阈值实现图像的二值化可动态观察其分割图像的具体结果。

API

全局阈值化:threshold

public static double threshold(Mat src, Mat dst, double thresh, double maxval, int type) 
  • 参数一:src,待二值化的多通道图像,只能是CV_8U和CV_32F两种数据类型

  • 参数二:dst,二值化后的图像,与输入图像具有相同的尺寸、类型和通道数

  • 参数三:thresh,二值化的阈值

  • 参数四:maxval,二值化过程的最大值,此函数只在THRESH_BINARY和THRESH_BINARY_INV两种二值化方法中才使用

  • 参数五:type,二值化类型

二值化类型

// C++: enum ThresholdTypes
public static final int
        THRESH_BINARY = 0,
        THRESH_BINARY_INV = 1,
        THRESH_TRUNC = 2,
        THRESH_TOZERO = 3,
        THRESH_TOZERO_INV = 4,
        THRESH_MASK = 7,
        THRESH_OTSU = 8,
        THRESH_TRIANGLE = 16;
类型 作用
THRESH_BINARY 0 灰度值大于阈值为最大值,其他值为0
THRESH_BINARY_INV 1 灰度值大于阈值为0,其他值为最大值
THRESH_TRUNC 2 灰度值大于阈值的为阈值,其他值不变
THRESH_TOZERO 3 灰度值大于阈值的不变,其他值为0
THRESH_TOZERO_INV 4 灰度值大于阈值的为零,其他值不变
THRESH_MASK 7 NA
THRESH_OTSU 8 大津法自动寻求全局阈值
THRESH_TRIANGLE 16 三角形法自动寻求全局阈值
c++获取图像在直方图_Android OpenCV(八):图像二值化_第1张图片 二值化类型 c++获取图像在直方图_Android OpenCV(八):图像二值化_第2张图片 生成方式

THRESH_OTSU和THRESH_TRIANGLE

这两种标志是获取阈值的方法,并不是阈值的比较方法的标志,这两个标志可以和前面5种标志一起使用,例如“THRESH_BINARY| THRESH_OTSU”。前面5种标志在调用函数时都需要人为的设置阈值,如果对图像不了解设置的阈值不合理,会对处理后的效果造成严重的影响,这两个标志分别表示利用大津法(OTSU)和三角形法(TRIANGLE)结合图像灰度值分布特性获取二值化的阈值,并将阈值以函数返回值的形式给出。因此如果函数最后一个参数设置了这两个标志中的任何一个,那么函数第三个参数thresh将由系统自动给出,但是在调用函数的时候仍然不能缺省,只是程序不会使用这个数值。目前,只有针对CV_8UC1图像可使用这两种标志位。

大津法(OTSU):最适用于双波峰

大津法又叫最大类间方差法、最大类间阈值法(OTSU)。它的基本思想是,用一个阈值将图像中的数据分为两类,一类中图像的像素点的灰度均小于这个阈值,另一类中的图像的像素点的灰度均大于或者等于该阈值。如果这两个类中像素点的灰度的方差越大,说明获取到的阈值就是最佳的阈值(方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,当部分前景错分为背景或部分背景错分为前景都会导致两部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。)。则利用该阈值可以将图像分为前景和背景两个部分。而我们所感兴趣的部分一般为前景。

三角形法(TRIANGLE):最适用于单个波峰,最开始用于医学分割细胞等

三角法求阈值最早见于Zack的论文《Automatic measurement of sister chromatid exchange frequency》主要是用于染色体的研究,该方法是使用直方图数据,基于纯几何方法来寻找最佳阈值,它的成立条件是假设直方图最大波峰在靠近最亮的一侧,然后通过三角形求得最大直线距离,根据最大直线距离对应的直方图灰度等级即为分割阈值,图示如下:

c++获取图像在直方图_Android OpenCV(八):图像二值化_第3张图片

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

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

c++获取图像在直方图_Android OpenCV(八):图像二值化_第4张图片

算法步骤

  1. 图像转灰度

  2. 计算图像灰度直方图

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

  4. 寻找直方图最大值

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

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

局部阈值化:adaptiveThreshold

局部自适应阈值是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。这样做的好处在于每个像素位置处的二值化阈值不是固定不变的,而是由其周围邻域像素的分布来决定的。亮度较高的图像区域的二值化阈值通常会较高,而亮度较低的图像区域的二值化阈值则会相适应地变小。不同亮度、对比度、纹理的局部图像区域将会拥有相对应的局部二值化阈值。

public static void adaptiveThreshold(Mat src, Mat dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C) 
  • 参数一:src,待二值化的图像,图像只能是CV_8UC1数据类型

  • 参数二:dst,二值化后的图像,与输入图像具有相同的尺寸、类型

  • 参数三:maxValue,二值化的最大值

  • 参数四:adaptiveMethod,自适应阈值算法,分为均值法ADAPTIVE_THRESH_MEAN_C和高斯法ADAPTIVE_THRESH_GAUSSIAN_C这两种。

  • 参数五:thresholdType,选择图像二值化方法的标志,只能是THRESH_BINARY和THRESH_BINARY_INV

  • 参数六:blockSize,自适应确定阈值的像素邻域大小,一般为3,5,7的奇数

  • 参数七:C,从平均值或者加权平均值中减去的常数,可以为正,也可以为负

自适应阈值算法

// C++: enum AdaptiveThresholdTypes
public static final int
        ADAPTIVE_THRESH_MEAN_C = 0,
        ADAPTIVE_THRESH_GAUSSIAN_C = 1;
类型 作用
ADAPTIVE_THRESH_MEAN_C 0 先求块中均值再减去C
ADAPTIVE_THRESH_GAUSSIAN_C 1 先求块中高斯加权和再减去C
c++获取图像在直方图_Android OpenCV(八):图像二值化_第5张图片 自适应阈值

操作

private fun threshold(type: Int) {
    val ret = Mat()
    Imgproc.threshold(mGray, ret, 127.toDouble(), 255.toDouble(), type)
    showMat(ret)
    title = getTypeName(type)
}
private fun adaptiveMean() {
    val ret = Mat()
    Imgproc.adaptiveThreshold(
        mGray,
        ret,
        255.toDouble(),
        Imgproc.ADAPTIVE_THRESH_MEAN_C,
        Imgproc.THRESH_BINARY,
        55,
        0.0
    )
    showMat(ret)
    title = "ADAPTIVE_THRESH_MEAN_C"
}

结果

运行结果

源码

https://github.com/onlyloveyd/LearningAndroidOpenCV

你可能感兴趣的:(c++获取图像在直方图)