美文网首页OpenCV 学习笔记
OpenCV 笔记(7):基于阈值的图像分割

OpenCV 笔记(7):基于阈值的图像分割

作者: fengzhizi715 | 来源:发表于2023-11-19 00:56 被阅读0次

1. 阈值分割

图像分割是图像进行视觉分析和模式识别的基本前提,而阈值分割是最简单的图像分割方法。阈值分割是基于灰度值或灰度值的特性来将图像直接划分为区域,实现简单而且计算速度快。

传统图像分割方法.png

1.1 threshold() 函数的5种处理类型

前面的文章提过,OpenCV 提供了基于灰度值的阈值分割函数 threshold(),在使用 threshold() 时先要将图像灰度化。

这个 threshold() 函数提供了 5 种阈值化类型。

  • THRESH_BINARY

将小于阈值的像素点灰度值置为0;大于阈值的像素点灰度值置为最大值(255)。

dst(x,y)= \left\{\begin{matrix} maxval, if src(x,y) > thresh\\ 0, otherwise \end{matrix}\right.

  • THRESH_BINARY_INV

将大于阈值的像素点灰度值置为0;小于阈值的像素点灰度值置为最大值(255)。

dst(x,y)= \left\{\begin{matrix} 0, if src(x,y) > thresh\\ maxval, otherwise \end{matrix}\right.

  • THRESH_TRUNC

小于阈值的像素点灰度值不变;大于阈值的像素点灰度值置为该阈值。

dst(x,y)= \left\{\begin{matrix} threshold, if src(x,y) > thresh\\ src(x,y) , otherwise \end{matrix}\right.

  • THRESH_TOZERO

大于阈值的像素点灰度值不变;小于阈值的像素点灰度值置为0

dst(x,y)= \left\{\begin{matrix} src(x,y), if src(x,y) > thresh\\ 0, otherwise \end{matrix}\right.

  • THRESH_TOZERO_INV

小于阈值的像素点灰度值不变;大于阈值的像素点置为0

dst(x,y)= \left\{\begin{matrix} 0, if src(x,y) > thresh\\ src(x,y), otherwise \end{matrix}\right.

下面的例子,通过获取图像的均值作为阈值,来分别展示这五种阈值分割的使用:

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat src = imread(".../landscape.jpg");
    imshow("src",src);

    Mat gray;
    cvtColor(src,gray,COLOR_BGR2GRAY);
    Scalar m = mean(gray);
    int thresh = m[0];

    Mat dst;
    threshold(gray, dst,thresh,255, THRESH_BINARY);
    imshow("thresh_binary",dst);

    threshold(gray, dst,thresh,255, THRESH_BINARY_INV);
    imshow("thresh_binary_inv",dst);

    threshold(gray, dst,thresh,255, THRESH_TRUNC);
    imshow("thresh_trunc",dst);

    threshold(gray, dst,thresh,255, THRESH_TOZERO);
    imshow("thresh_tozero",dst);

    threshold(gray, dst,thresh,255, THRESH_TOZERO_INV);
    imshow("thresh_tozero_inv",dst);

    waitKey(0);
    return 0;
}
五种阈值分割(一).png 五种阈值分割(二).png

THRESH_BINARY 和 THRESH_BINARY_INV 可以通过阈值分割将灰度图像转变成二值图像。而通过其他的阈值方式进行分割,仍然得到灰度图像。

2. 全局阈值分割

对图像进行灰度化之后,若图像中的目标和背景具有不同的灰度集合,且这两个灰度集合可用一个灰度级阈值 T 进行分割。

分割后的图像 g(x,y) 满足:

g(x,y)= \left\{\begin{matrix} 1, if src(x,y) > T\\ 0, otherwise \end{matrix}\right.

当 T 是一个适用于整个图像的常数时,称为全局阈值分割。

在大多数情况下,图像之间会有较大变化,即使全局阈值分割是一种合适的方法,也需要有能对每幅图像自动估计阈值的算法。

2.1 OTSU 算法

OTSU 算法,是1979年由日本学者大津提出的,也被称为大津算法、最大类间差法。OTSU 算法在类间方差最大的情况下是最佳的,完全以在一幅图像的直方图上执行计算为基础。

直方图是一种常用的数据统计图。对某一物理或特征量不同取值,找出它的最大值和最小值,然后确定一个区间,使其包含全部测量数据,将区间分成若干小区间,统计测量结果出现在各小区间的频数或占比,以测量数据为横坐标,以频数或占比为纵坐标,划出各小区间及其对应的频数或占比高度,则可得到一个矩形图。

OTSU 算法的基本思想是根据选取的阈值将图像分为目标和背景两个部分,计算该灰度值下的类间方差值。当类间方差最大时,对应的灰度值作为最佳阈值。

算法的推导过程如下:

  1. 假设 M*N 尺寸的图像的灰度值区间为[0,m],t 为阈值,它将图像分为目标和背景两个部分。即为灰度值为 [0,t] 的背景以及灰度值为 [t+1,m] 的目标两部分。其中,背景的像素点为N_0,目标的像素点为N_1

  2. 每一部分的平均灰度值\mu_0、\mu_1,计算每一部分的所占比例w_0、w_1,以及总的平均灰度值\mu

w_0 = N_0/ M*N

w_1 = N_1/M*N

并且 N_0 + N_1 = M*N

则,w_0 + w_1 = 1

\mu = \frac{\mu_0*N_0 + \mu_1*N_1}{M*N} = \mu_0*w_0 + \mu_1*w_1

  1. 计算他们的类间方差

\delta^2 = w_0*(\mu_0-\mu)^2+w_1*(\mu_1-\mu)^2=w_0*w_1*(\mu_0-\mu_1)^2

  1. 通过遍历,获得类间方差最大时对应的阈值t作为我们最终所取的阈值。

背景和目标之间的类间方差越大,则构成图像的这两部分的差别越大,因此可以将这个阈值来进行全局的阈值分割。

下面的例子,使用 OTSU 算法来计算阈值,并且将阈值分割后的二值图像展示出来。

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat src = imread(".../landscape.jpg");
    imshow("src",src);

    Mat gray;
    cvtColor(src,gray,COLOR_BGR2GRAY);

    Mat dst;
    int thresh = threshold(gray, dst,0,255, THRESH_BINARY | THRESH_OTSU);
    imshow("thresh_ostu",dst);

    cout << "thresh = " << thresh << endl;

    waitKey(0);
    return 0;
}

输出结果:

thresh = 118
ostu.png

在使用 threshold() 函数时,如果使用 THRESH_BINARY类型来进行阈值分割,通常需要黑色的背景。如果是白色的背景,则需要使用 THRESH_BINARY_INV类型。例如下面的图像是白色的背景:

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat src = imread(".../phone.jpg");
    imshow("src",src);

    Mat gray;
    cvtColor(src,gray,COLOR_BGR2GRAY);

    Mat dst;
    threshold(gray, dst,0,255, THRESH_BINARY_INV | THRESH_OTSU);
    imshow("dst",dst);

    waitKey(0);
    return 0;
}
THRESH_BINARY_INV.png

2.2 Triangle 算法

OTSU 算法是针对直方图中有两个波峰的情况,效果会比较好。但是针对直方图中只有一个波峰的情况,则 Triangle 算法会比较好。

它的成立条件是假设直方图最大波峰在靠近最亮的一侧,然后通过三角形求得最大直线距离,根据最大直线距离对应的直方图灰度值即为阈值。

Triangle算法.png

下面的例子,使用 Triangle 算法来计算阈值,并且将阈值分割后的二值图像展示出来。

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat src = imread(".../landscape.jpg");
    imshow("src",src);

    Mat gray;
    cvtColor(src,gray,COLOR_BGR2GRAY);

    Mat dst;
    int thresh = threshold(gray, dst,0,255, THRESH_BINARY | THRESH_TRIANGLE);
    imshow("thresh_triangle",dst);

    cout << "thresh = " << thresh << endl;

    waitKey(0);
    return 0;
}

输出结果:

thresh = 80
triangle.png

3. 局部阈值分割

我们在使用灰度阈值分割图像的时候,会受到噪声、光照、反射的影响。在这种情况下,整幅图像用一个固定的阈值来分割,可能得不到好的分割效果。我们可以对图像降噪,以及使用可变阈值近似处理照明和反射引起的不均匀性。

后续的文章会单独介绍如何对图像降噪,在这里我们介绍一种自适应阈值分割的方法。它的阈值是根据图像上的每一个小区域计算与其对应的阈值,在同一幅图像上的不同区域采用的是不同的阈值,根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。

它的优点:

  1. 每个像素位置处的二值化阈值是由其周围邻域像素的分布来决定的。

  2. 不同亮度、对比度、纹理的局部图像区域将会拥有相对应的局部二值化阈值。

  3. 适合处理光照不均的图像。

下面的例子分别使用 OTSU 算法和自适应阈值分割来实现二值化,其中 OpenCV 提供了 adaptiveThreshold() 函数实现自适应阈值分割。

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat src = imread(".../viaduct.jpg");
    imshow("src",src);

    Mat gray;
    cvtColor(src,gray,COLOR_BGR2GRAY);

    Mat dst;
    threshold(gray, dst,0,255, THRESH_BINARY | THRESH_OTSU);
    imshow("thresh_ostu",dst);

    adaptiveThreshold(gray, dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 11, 2);
    imshow("adaptiveThreshold",dst);

    waitKey(0);
    return 0;
}
ostu和自适应阈值分割(一).png ostu和自适应阈值分割(二).png

稍微对 adaptiveThreshold() 函数的参数做一下解释:

第四个参数 adaptiveMethod:指定自适应阈值算法。

  • ADAPTIVE_THRESH_MEAN_C:局部邻域块的平均值。该算法是先求出块中的均值,再减去常数 c。

  • ADAPTIVE_THRESH_GAUSSIAN_C:局部邻域块的高斯加权和。该算法是在区域中 (x,y) 周围的像素根据高斯函数按照他们离中心点的距离进行加权计算, 再减去常数 c。

第六个参数 blockSize:邻域块大小。

第七个参数 c:与算法有关的参数,阈值就等于计算出的平均值或者加权平均值减去这个常数,c 可以是负数。

4. 总结

本文介绍了传统图像分割的方法,主要是介绍了基于灰度图像的阈值分割。

阈值分割并不等同于图像的二值化, threshold() 函数有五种阈值类型,它适合全局的阈值分割。对于光照不均的图像可以采用 adaptiveThreshold() 函数进行自适应阈值分割。

相关文章

  • pyrMeanShiftFiltering

    图像分割学习笔记_1(opencv自带meanshift分割例子) Opencv均值漂移pyrMeanShiftF...

  • OpenCV Python 图像 阈值分割

    src 源图像 参考资料:https://study.163.com/course/courseLearn.htm...

  • LabVIEW彩色图像分割(基础篇—14)

    基于目标颜色的彩色图像分割常包括色彩阈值处理(Color Threshold)和色彩分割(Color Segmen...

  • 阈值化

    OpenCV-图像阈值-简单阈值、自适应阈值、Otsu’s 二值化

  • 基于遗传算法和大津阈值分割法实现的图像分割

    一、简述 本实验采用遗传算法和大津阈值分割法确定图像分割的最佳阈值,从而对图像进行二值化分割。 二、大津阈值分割法...

  • 三 (3.2 imgproc) 图像阈值操作

    阈值操作原理: 什么是阈值? 最简单的图像分割的方法。 应用举例:从一副图像中利用阈值分割出我们需要的物体部分(当...

  • 基于阈值方法分割彩色图像

    图像阈值分割 1.查看图像直方图 2.选择合适的阈值 3.利用形态学操作,填补局部空洞 4.掩膜,得到图像结果 查...

  • 分水岭

    OpenCV学习(7) 分水岭算法(1) Opencv(二)—图像分割之分水岭算法! OpenCV---分水岭算法

  • Opencv之图像分割

    1、阈值分割 1.1 简介 图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单、计算量小、性能较稳定而成...

  • 图像分割

    1、阈值分割 1.1 简介 图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单、计算量小、性能较稳定而成...

网友评论

    本文标题:OpenCV 笔记(7):基于阈值的图像分割

    本文链接:https://www.haomeiwen.com/subject/iiipwdtx.html