昊虹AI笔记网

 找回密码
 立即注册
搜索
查看: 1008|回复: 0
收起左侧

图像的OTSU阈值化、双阈值化、半阈值化的原理及OpenCV代码实现

[复制链接]

239

主题

241

帖子

931

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
931
昊虹君 发表于 2022-12-13 12:11 | 显示全部楼层 |阅读模式
图像的OTSU阈值化、双阈值化、半阈值化的原理及OpenCV代码实现

本文的基础是OpenCV的函数threshold(),关于函数threshold()的详细介绍,大家可以参考下面这篇博文:
https://www.hhai.cc/thread-162-1-1.html

一、图像的OTSU阈值化
在阈值化处理中,OTSU是一种常用的算法,中文译为大津法或最大类间方差法。
它是1979年由日本学者大津展之提出的一种对图像进行二值化的高效算法,是在判别与最小二乘法原理的基础上推导出来的。
它的基本思路是把直方图在某一阈值处分割成两组,当被分成的两组间方差为最大时,这一阈值为“决定阈值”。

OTSU的算法步骤如下:
⑴统计灰度级中每个像素在整幅图像中的个数。
⑵计算每个像素在整幅图像的概率分布。
⑶对灰度级进行遍历搜索,计算当前灰度值下前景背景类间概率。
⑷通过目标函数计算出类内与类间方差下对应的阈值。
更具体的原理介绍大家可以参考下面这篇博文:
https://blog.csdn.net/weixin_44227356/article/details/116031602

图像的OTSU阈值化可以直接由函数threshold()实现,具体实现的代码我已经写在下面这篇博文中了:
https://www.hhai.cc/thread-162-1-1.html【打开页面后搜索“当type取值为THRESH_OTSU 、THRESH_TRIANGLE时的示例代码”】

在上面链接的博文中我已经说了,函数threshold()的Python版才会返回进行阈值化时所使用的阈值,C++版本则不会,所以在OpenCV-C++环境下,函数threshold()使用OTSU算法进行阈值化处理时用的阈值我们是不知道的。如果想知道OTSU算法的阈值是多少,只有自己根据OTSU算法的原理写出代码进行计算。
这里昊虹君根据OTSU算法的原理写了一个计算OTSU阈值计算的函数,代码如下:

下面的示例代码中用到的图像下载链接如下:
https://pan.baidu.com/s/1PqeJG6EbONmYg03jYQb2MQ?pwd=xu7b
[C++] 纯文本查看 复制代码
//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术

//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询

//OpenCV版本 OpenCV3.0

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int OTSU(cv::Mat srcImage)
{
	int nCols = srcImage.cols;
	int nRows = srcImage.rows;
	int threshold = 0;
	// 初始化统计参数
	int nSumPix[256];
	float nProDis[256];
	for (int i = 0; i < 256; i++)
	{
		nSumPix[i] = 0;
		nProDis[i] = 0;
	}
	// 统计灰度级中每个像素在整幅图像中的个数 
	for (int i = 0; i < nRows; i++)
	{
		for (int j = 0; j < nCols; j++)
		{
			nSumPix[(int)srcImage.at<uchar>(i, j)]++;
		}
	}
	// 计算每个灰度级占图像中的概率分布
	for (int i = 0; i < 256; i++)
	{
		nProDis[i] = (float)nSumPix[i] / (nCols * nRows);
	}
	// 遍历灰度级[0,255],计算出最大类间方差下的阈值  
	float w0, w1, u0_temp, u1_temp, u0, u1, delta_temp;
	double delta_max = 0.0;
	for (int i = 0; i < 256; i++)
	{
		// 初始化相关参数
		w0 = w1 = u0_temp = u1_temp = u0 = u1 = delta_temp = 0;
		for (int j = 0; j < 256; j++)
		{
			//背景部分 
			if (j <= i)
			{
				// 当前i为分割阈值,第一类总的概率  
				w0 += nProDis[j];
				u0_temp += j * nProDis[j];
			}
			//前景部分   
			else
			{
				// 当前i为分割阈值,第一类总的概率
				w1 += nProDis[j];
				u1_temp += j * nProDis[j];
			}
		}
		// 分别计算各类的平均灰度 
		u0 = u0_temp / w0;
		u1 = u1_temp / w1;
		delta_temp = (float)(w0 *w1* pow((u0 - u1), 2));
		// 依次找到最大类间方差下的阈值    
		if (delta_temp > delta_max)
		{
			delta_max = delta_temp;
			threshold = i;
		}
	}
	return threshold;
}


int main()
{
	// 读取源图像
	cv::Mat image = cv::imread("F:/material/images/2022/2022-06/img_300_320.jpg", 1);
	if (image.empty())
		return -1;

	//imshow("image", image);

	Mat img_gray;

	cvtColor(image, img_gray, COLOR_BGR2GRAY);
	imshow("img_gray", img_gray);

	int OTSU_threshold;
	OTSU_threshold = OTSU(img_gray);

	cout << "OTSU_threshold is: " << OTSU_threshold << endl << endl;


	cv::waitKey(0);
	return 0;
}

运行结果如下:

上面的阈值与我们在Python-OpenCV环境下使用函数threshold()得到的OTSU阈值是一样的:

上面这张截图来源于下面这篇博文:
https://www.hhai.cc/thread-162-1-1.html

二、图像的双阈值化
有时候图像中有明显的双分界特征,我们考虑用双阈值法进行阈值化操作。
所谓双阈值化是指对于两个阈值thresh1<thresh2,将大于thresh1且小于thresh2的灰度值设定为maxval,其余的灰度值设为0。
其数学表达式如下:

可以用函数threshold()实现图像的双阈值化,其C++代码如下:
[C++] 纯文本查看 复制代码
//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术

//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询

//OpenCV版本 OpenCV3.0

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;


int main()
{

	cv::Mat A1 = (cv::Mat_<uchar>(2, 3) << 0, 10, 50,
		80, 150, 255);
	cout << "A1中的数据为:\n" << A1 << endl << endl;

	double low_threshold = 70;
	double high_threshold = 180;

	double maxval1 = 222;

	Mat B1, B2, B3, B4, B5;

	// 小阈值对图像或矩阵进行阈值化操作
	threshold(A1, B1, low_threshold, maxval1, THRESH_BINARY);
	cout << "B1中的数据为:\n" << B1 << endl << endl;

	// 大阈值对图像或矩阵进行阈值化操作
	threshold(A1, B2, high_threshold, maxval1, THRESH_BINARY_INV);
	cout << "B2中的数据为:\n" << B2 << endl << endl;

	// 矩阵与运算得到阈值化结果
	cv::bitwise_and(B1, B2, B3);
	cout << "B3中的数据为:\n" << B3 << endl << endl;


	return(0);
}

运行结果如下图所示:

从上面的运行结果我们可以看出,原矩阵中介于阈值70到180之间的灰度值被置为了222,其它灰度值被置为了0。

二、图像的半阈值化
如果图像中有明显的目标与背景的差异特征,那么进行阈值化操作时,可以使用半阈值化进行操作。
半阈值化操作时,大于thresh的灰度值不变,其余灰度值置为0。
半阈值化的数学表达式如下:

函数threshold(),当参数type取值为“THRESH_TOZERO”时,实际上就是作的图像的半阈值化操作。
示例代码之前在博文https://www.hhai.cc/thread-162-1-1.html中就已经给出了,这里就不再给了。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|昊虹AI笔记网 ( 蜀ICP备2024076726 )

GMT+8, 2024-5-17 20:07 , Processed in 0.026665 second(s), 23 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表