【小白】Open-CV 学习笔记 - 6.7阈值化

阈值化

阈值化操作在图像处理中是一种常用的算法,比如图像的二值化就是一种最常见的一种阈值化操作。opencv2和opencv3中提供了直接阈值化操作cv::threshold()和自适应阈值化操作cv::adaptiveThreshold()两种阈值化操作接口,这里将对这两个接口进行介绍和对比。

1、直接阈值化——cv::threshold()

阈值化操作的基本思想是,给定一个输入数组和一个阈值,数组中的每个元素将根据其与阈值之间的大小发生相应的改变。opencv3中支持这一操作的接口是cv::threshold(),具体调用方法如下:

double cv::threshold(
cv::InputArray src, // 输入图像
cv::OutputArray dst, // 输出图像
double thresh, // 阈值
double maxValue, // 向上最大值
int thresholdType // 阈值化操作的类型
);

1、两种阈值的寻找方法
THRESH_OTSU(根据给的数自动计算阈值),THRESH_TRIANGLE(根据直方图计算阈值)

2、五种阈值的处理方法
如下表所示,每一种阈值化操作类型,对应着一种源图像上每一个像素点与阈值thresh之间比较操作。根据源图像像素和阈值之间的大小关系,目标像素可能被置为0、原像素值、或者设定的最大值maxValue。
在这里插入图片描述
下图将会帮助大家理解不同的阈值化类型所表示的确切含义。
在这里插入图片描述

void test_threshold()
{
	cv::Mat src = cv::imread("lena.jpg", cv::IMREAD_GRAYSCALE);
	cv::Mat dst;
 
	double thresh = 100;
	int maxVal = 255;
	cv::threshold(src, dst, thresh, maxVal, cv::THRESH_BINARY);
 
	cv::imshow("threshold", dst);
	cv::waitKey(0);
 
	return;
}

实用上面的代码进行阈值化处理,原图和五种不同方式阈值化的结果分别如下:
在这里插入图片描述
这里着重讲一下OTSU算法

  • Otsu法
    (最大类间方差法,有时也称之为大津算法)使用的是聚类的思想,把图像的灰度数按灰度级分成2个部分,使得两个部分之间的灰度值差异最大,每个部分之间的灰度差异最小,通过方差的计算来寻找一个合适的灰度级别来划分。所以可以在二值化的时候采用otsu算法来自动选取阈值进行二值化。otsu算法被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。因此,使类间方差最大的分割意味着错分概率最小。
  • Otsu流程:
  1. 先計算影像的直方圖
  2. 把直方圖強度大於閾值的像素分成一組,把小於閾值的像素分成另一組。
  3. 分別計算這兩組的組內變異數,並把兩個組內變異數相加。
  4. 將0~255依序當作閾值來計算組內變異數和,總和值最小的就是結果閾值。
  • OpenCV自適應閾值二值化:

一樣是用threshold()函式,使用方式也一樣,只是最後一個參數增加CV_THRESH_OTSU,目前otsu只能使用在8位元圖。

  • double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)
  • src:輸入圖,只能輸入單通道。
  • dst:輸出圖,尺寸大小、深度會和輸入圖相同。
  • thresh:閾值。
  • maxval:二值化結果的最大值。
  • type:有THRESH_BINARY、THRESH_BINARY_INV、THRESH_TRUNC、THRESH_TOZERO、THRESH_TOZERO_INV五種。type從上述五種結合CV_THRESH_OTSU,類似寫成:THRESH_BINARY | CV_THRESH_OTSU。

值得一说的是threshold_type使用CV_THRESH_OTSU类型,这样该函数就会使用大律法OTSU得到的全局自适应阈值来进行二值化图片,而参数中的threshold不再起作用。

该算法的理论依据是:假定图像包含两类像素(前景像素和背景像素),直方图为双峰直方图,然后计算使得两类像素能分开的最佳阈值(类内方差),或等价的间类间方差最大。
该算法的主要思想是,在进行阈值化时,考虑所有可能的阈值,分别计算低于阈值和高于阈值像素的方差,使下式最小化的值作为阈值:
在这里插入图片描述

其中,两类像素方差的权值由两类像素的个数决定。这种阈值化的结果相对来说比较理想,可以避免寻找合适阈值的操作,但是这种方式运算量较大,费时。处理的结果如下:
在这里插入图片描述
但是,直接阈值化操作是一种一刀切的方式,对于亮度分布差异较大的图像,常常无法找到一个合适的阈值。如下所示,对棋盘格进行二值化操作,由于图像右上角区域和图像下部的亮度差异较为大,无法找到一个合适的阈值,将棋盘上的所有棋盘格给区分开来。
在这里插入图片描述
针对于上述情况,我们需要一种改进的阈值化算法,即自适应阈值化。

2、自适应阈值化——cv::adaptiveThreshold()

自适应阈值化能够根据图像不同区域亮度分布的,改变阈值,具体调用方法如下:

void cv::adaptiveThreshold(
cv::InputArray src, // 输入图像
cv::OutputArray dst, // 输出图像
double maxValue, // 向上最大值
int adaptiveMethod, // 自适应方法,平均或高斯
int thresholdType // 阈值化类型
int blockSize, // 块大小
double C // 常量
);

cv::adaptiveThreshold()支持两种自适应方法,即cv::ADAPTIVE_THRESH_MEAN_C(平均)和cv::ADAPTIVE_THRESH_GAUSSIAN_C(高斯)。在两种情况下,自适应阈值T(x, y)。通过计算每个像素周围bxb大小像素块的加权均值并减去常量C得到。其中,b由blockSize给出,大小必须为奇数;如果使用平均的方法,则所有像素周围的权值相同;如果使用高斯的方法,则(x,y)周围的像素的权值则根据其到中心点的距离通过高斯方程得到。

扫描二维码关注公众号,回复: 8517460 查看本文章

测试代码如下:

void test_adaptive_threshold()
{
cv::Mat src = cv::imread("chessboard.png", cv::IMREAD_GRAYSCALE);
cv::Mat dst;
int maxVal = 255;
int blockSize = 41;
double C = 0;
cv::adaptiveThreshold(src, dst, maxVal, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, blockSize, C);
cv::imshow("threshold", dst);
cv::waitKey(0);
return;
}

我们分别使用了平均和高斯两种自适应方法,结果如下:
在这里插入图片描述

发布了34 篇原创文章 · 获赞 8 · 访问量 1883

猜你喜欢

转载自blog.csdn.net/weixin_43583163/article/details/97182571