图像分割/二值化
文章目录
0.简介
图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术。它特别适用于目标和背景占据不同灰度级范围的图像。它不仅可以极大的压缩数据量,而且也大大简化了分析和处理步骤,因此在很多情况下,是进行图像分析、特征提取与模式识别之前的必要的图像预处理过程。图像阈值化的目的是要按照灰度级,对像素集合进行一个划分,得到的每个子集形成一个与现实景物相对应的区域,各个区域内部具有一致的属性,而相邻区域不具有这种一致属性。这样的划分可以通过从灰度级出发选取一个或多个阈值来实现。
1.学习目标
- 了解阈值分割基本概念
- 理解最大类间方差法(大津法)、自适应阈值分割的原理
- 掌握OpenCV框架下上述阈值分割算法API的使用
2.算法理论介绍
2.1.最大类间方差法
大津法 O T S U OTSU OTSU 是一种确定图像二值化分割阈值的算法,由日本学者大津于1979年提出。从大津法的原理上来讲,该方法又称作最大类间方差法,因为按照大津法求得的阈值进行图像二值化分割后,前景与背景图像的类间方差最大。
它被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响,因此在数字图像处理上得到了广泛的应用。它是按图像的灰度特性,将图像分成背景和前景两部分。因方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,当部分前景错分为背景或部分背景错分为前景都会导致两部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。
应用
是求图像全局阈值的最佳方法,应用不言而喻,适用于大部分需要求图像全局阈值的场合。
优点
计算简单快速,不受图像亮度和对比度的影响。
缺点
对图像噪声敏感;只能针对单一目标分割;当目标和背景大小比例悬殊、类间方差函数可能呈现双峰或者多峰,这个时候效果不好。
原理非常简单,涉及的知识点就是均值、方差等概念和一些公式推导。为了便于理解,我们从目的入手,反推一下这著名的OTSU算法。
求类间方差:
OTSU算法的假设是存在阈值 T H TH TH将图像所有像素分为两类 C 1 C1 C1(小于TH)和 C 2 C2 C2(大于TH),则这两类像素各自的均值就为 m 1 m1 m1、 m 2 m2 m2,图像全局均值为 m G mG mG。同时像素被分为 C 1 C1 C1和 C 2 C2 C2类的概率分别为 p 1 p1 p1、 p 2 p2 p2。因此就有:
p 1 ∗ m 1 + p 2 ⋆ m 2 = m G (1) \begin{array}{c} \mathrm{p} 1^{*} \mathrm{m} 1+\mathrm{p} 2^{\star} \mathrm{m} 2=\mathrm{mG} \tag1 \end{array} p1∗m1+p2⋆m2=mG(1)p 1 + p 2 = 1 (2) \mathrm{p} 1+\mathrm{p} 2=1 \tag2 p1+p2=1(2)
根据方差的概念,类间方差表达式为:
σ 2 = p 1 ( m 1 − m G ) 2 + p 2 ( m 2 − m G ) 2 (3) \sigma^{2}=p 1(m 1-m G)^{2}+p 2(m 2-m G)^{2}\tag3 σ2=p1(m1−mG)2+p2(m2−mG)2(3)
我们将上式化简,将式(1)代入式(3),可得:
σ 2 = p 1 p 2 ( m 1 − m 2 ) 2 (4) \sigma^{2}=p 1 p 2(m 1-m 2)^{2}\tag4 σ2=p1p2(m1−m2)2(4)
其中:
p 1 = ∑ i = 0 k p i (5) p 1=\sum_{i=0}^{k} p_{i}\tag5 p1=i=0∑kpi(5)m 1 = 1 / p 1 ∗ ∑ i = 0 k i p i (6) m 1=1 / p 1 * \sum_{i=0}^{k} i p_{i}\tag6 m1=1/p1∗i=0∑kipi(6)
m 2 = 1 / p 2 ∗ ∑ i = k + 1 L − 1 i p i (7) m 2=1 / p 2 * \sum_{i=k+1}^{L-1} i p_{i}\tag7 m2=1/p2∗i=k+1∑L−1ipi(7)
照着公式,遍历0~255个灰度级,求出使式(4)最大的 K K K就OK。
根据原文,式(4)还可以进一步变形:
首先灰度级K的累加均值 m m m和图像全局均值 m G mG mG分别为:
m = ∑ i = 0 k i p i (8) m=\sum_{i=0}^{k} i p_{i}\tag8 m=i=0∑kipi(8)m G = ∑ i = 0 L − 1 i p i (9) m G=\sum_{i=0}^{L-1} i p_{i}\tag9 mG=i=0∑L−1ipi(9)
再通过式(6): m 1 m1 m1、 m 2 m2 m2就可变为:
m 1 = 1 / p 1 ∗ m (10) m 1=1 / p 1 * m\tag{10} m1=1/p1∗m(10)m 2 = 1 / p 2 ∗ ( m G − m ) (11) m 2=1 / p 2 *(m G-m)\tag{11} m2=1/p2∗(mG−m)(11)
式(10)、(11)代入式(4),我们可得原文最终的类间方差公式:
σ 2 = ( m G ∗ p 1 − m ) 2 p 1 ( 1 − p 1 ) (12) \sigma^{2}=\frac{(m G * p 1-m)^{2}}{p 1(1-p 1)}\tag{12} σ2=p1(1−p1)(mG∗p1−m)2(12)
根据公式(5)、(8)、(9)求能使得上式(12)最大化的灰度级k就是 O T S U OTSU OTSU阈值
2.2自适应阈值
前面介绍了 O T S U OTSU OTSU算法,但这算法属于全局阈值法,所以对于某些光照不均的图像,这种全局阈值分割的方法会显得苍白无力
自适应阈值法 ( a d a p t i v e T h r e s h o l d ) (adaptiveThreshold) (adaptiveThreshold),它的思想不是计算全局图像的阈值,而是根据图像不同区域亮度分布,计算其局部阈值,所以对于图像不同区域,能够自适应计算不同的阈值,因此被称为自适应阈值法。(其实就是局部阈值法)
3.基于Opencv的实现
3.1函数原型
3.1.1最大类间方差法threshold
double cv::threshold( InputArray src,
OutputArray dst,
double thresh, //阈值
double maxval, //向上最大值
int type) //阈值化操作类型
Enumerator | |
---|---|
THRESH_BINARY | dst ( x , y ) = { maxval if src ( x , y ) > thresh 0 otherwise \operatorname{dst}(x, y)=\left\{\begin{array}{ll}\text {maxval} & \text { if } \operatorname{src}(x, y)>\text {thresh} \\0 & \text {otherwise}\end{array}\right. dst(x,y)={maxval0 if src(x,y)>threshotherwise |
THRESH_BINARY_INV | dst ( x , y ) = { 0 if src ( x , y ) > thresh maxval otherwise \operatorname{dst}(x, y)=\left\{\begin{array}{ll}0 & \text { if } \operatorname{src}(x, y)>\text { thresh } \\\text {maxval} & \text { otherwise }\end{array}\right. dst(x,y)={0maxval if src(x,y)> thresh otherwise |
THRESH_TRUNC | dst ( x , y ) = { threshold if src ( x , y ) > thresh src ( x , y ) otherwise \operatorname{dst}(x, y)=\left\{\begin{array}{ll}\text {threshold} & \text { if } \operatorname{src}(x, y)>\text {thresh} \\\operatorname{src}(x, y) & \text { otherwise }\end{array}\right. dst(x,y)={thresholdsrc(x,y) if src(x,y)>thresh otherwise |
THRESH_TOZERO | dst ( x , y ) = { src ( x , y ) if src ( x , y ) > thresh 0 otherwise \operatorname{dst}(x, y)=\left\{\begin{array}{ll}\operatorname{src}(x, y) & \text { if } \operatorname{src}(x, y)>\text { thresh } \\0 & \text { otherwise }\end{array}\right. dst(x,y)={src(x,y)0 if src(x,y)> thresh otherwise |
THRESH_TOZERO_INV | dst ( x , y ) = { 0 if src ( x , y ) > thresh src ( x , y ) otherwise \operatorname{dst}(x, y)=\left\{\begin{array}{ll}0 & \text { if } \operatorname{src}(x, y)>\text { thresh } \\ \operatorname{src}(x, y) & \text { otherwise }\end{array}\right. dst(x,y)={0src(x,y) if src(x,y)> thresh otherwise |
THRESH_MASK | |
THRESH_OTSU | flag, use Otsu algorithm to choose the optimal threshold value |
THRESH_TRIANGLE | flag, use Triangle algorithm to choose the optimal threshold value |
3.1.2自适应阈值adaptiveThreshold
void adaptiveThreshold( InputArray src,
OutputArray dst,
double maxValue, //预设满足条件的最大值
int adaptiveMethod, //指定自适应阈值算法
int thresholdType, //指定阈值类型
int blockSize, // 表示领域块大小
double C) // 参数C表示与算法有关的参数
//----------------------------------------------------------------------
算法类型
ADAPTIVE_THRESH_MEAN_C // 局部领域块平均值,该算法先求出块中的均值,在减去常熟C
ADAPTIVE_THRESH_GAUSSIAN_C // 局部领域块的高斯加权和,在区域中(x,y)周围的像素根据高斯函数按照它们离 中心点的距离进行加权计算,再减去常熟C
阈值类型
THRESH_BINARY //二进制阈值
THRESH_BINARY_INV //反二进制阈值
3.2实现示例(c++)
3.2.1大津阈值
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
Mat img = imread("C:/Users/Administrator/Desktop/opencv/test1.jpg");
if (img.empty())
{
cout << "Error: Could not load image" << endl;
return 0;
}
Mat gray;
cvtColor(img, gray, COLOR_RGB2GRAY);
Mat dst;
threshold(gray, dst, 0, 255,THRESH_OTSU);
imshow("src", img);
imshow("gray", gray);
imshow("dst", dst);
waitKey(0);
return 0;
}
3.2.2自适应阈值
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
Mat img = imread("C:/Users/Administrator/Desktop/opencv/test1.jpg");
if (img.empty())
{
cout << "Error: Could not load image" << endl;
return 0;
}
Mat gray;
cvtColor(img, gray, COLOR_RGB2GRAY);
Mat dst;
adaptiveThreshold(gray, dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 21, 10);
imshow("src", img);
imshow("gray", gray);
imshow("dst", dst);
waitKey(0);
return 0;
}