C++实现大津二值化算法


一、大津算法

大津算法(Ostu)也称最大类间方差法。顾名思义,就使两个类别之间某个属性的方差最大的方法。在图像处理中,灰度分布均匀性作为区分图像各个区域的一种度量,背景和目标之间的灰度均值的方差越大,说明目标与背景差别越大。由于其不受图像对比度的影响,大津二值化常被用来分割目标与背景。

二、原理

对于一副大小RC的图像,目标和背景的分割阈值记作T,图像中像素的灰度值小于阈值T的像素个数记作N0。
1.目标的像素点数占整幅图像的比例记为ω0,ω0=N0/ C×R,平均灰度μ0。
2.背景像素点数占整幅图像的比例为ω1, ω1=N0/ C×R,平均灰度μ1。
3.图像的总平均灰度记为μ,       μ=ω0
μ0+ω1*μ1 (1)
4.类间方差记为g, g = w0 * (u0 - u)^2 + w1 * (u1 - u)^2。 (2)
5.     N0+N1=M×N
      ω0+ω1=1 
将式(1)代入式(2),得到等价公式:g=ω0ω1(μ0-μ1)^2 (3)
公式(3)便是最大类间方差法。

三.算法实现步骤

1.统计图像中各个灰度值的个数:

	const int nGrayScale = 256;//灰度
	int nPixelCount[nGrayScale] = { 0 };//灰度直方图
	//统计图片中各个灰度值的个数
	for (int y = 0; y < src.rows; ++y)
	{
		for (int x = 0; x < src.cols; ++x)
		{
			int val = src.at<uchar>(y, x);
			nPixelCount[val]++;
		}
	}

2.统计图片中各个灰度值所占的比例:

	//统计图片中各个灰度值所占的比例
	int nPixelSum = src.rows * src.cols;//总像素值
	float fPixelPct[nGrayScale] = { 0 };//各个灰度值占总体的比例

	for (int i = 0; i < nGrayScale; ++i)
	{
		fPixelPct[i] = 1.0 *nPixelCount[i] / nPixelSum;
	}

3.从灰度0迭代到灰度255,每次迭代计算能使公式(3)最大的阈值Threshed:

	double w0, w1;//背景/目标像素占比
	double u0, u1;//目标/背景平均灰度值
	double fTempVar = 0;//类间方差
	double fMaxVar = 0;//最大类间方差
	double fBestValue = 0;//最优阈值
	double fTemp0, fTemp1;

	for (int k = 0; k < nGrayScale; ++k)
	{
		w0 = w1 = u0 = u1 = fTempVar = 0;
		fTemp0 = fTemp1 = 0;
		//前景,背景区分 [0-k][k+1-255]
		for (int i = 0; i < nGrayScale; ++i)
		{
			//如果当前像素值小于阈值k则属于背景,反之属于目标
			if (i <= k)
			{
				//计算背景像素占比
				w0 += fPixelPct[i];
				//计算当前灰度值发生的概率:灰度值*灰度值发生的概率
				fTemp0 += (i * fPixelPct[i]);

			}
			else
			{
				//计算背景像素占比
				w1 += fPixelPct[i];
				fTemp1 += (i * fPixelPct[i]);
			}
		}
		//计算平均灰度值:p0/w0
		u0 = fTemp0 / w0;
		u1 = fTemp1 / w1;

		//计算类内方差
		fTempVar = (float)(w0 * w1 * pow((u0 - u1), 2));
		if (fTempVar > fMaxVar)
		{
			fMaxVar = fTempVar;
			fBestValue = k;

		}
	}

四.测试结果

font color=#999AAA >测试采用了三张目标与背景对比度不一样的图片。
图片1:
在这里插入图片描述
图片2:
在这里插入图片描述
图片3:
在这里插入图片描述
实验证明,大津二值化可以不受图像对比度的影响,自动分割目标与背景。

五.完整代码

	//原图
	Mat src = imread("image4.PNG", IMREAD_GRAYSCALE);
	//输出图
	Mat dst = Mat::zeros(src.size(), src.type());


	const int nGrayScale = 256;//灰度
	int nPixelCount[nGrayScale] = { 0 };//灰度直方图
	//统计图片中各个灰度值的个数
	for (int y = 0; y < src.rows; ++y)
	{
		for (int x = 0; x < src.cols; ++x)
		{
			int val = src.at<uchar>(y, x);
			nPixelCount[val]++;		//int nPixelCount[nGrayScale] = { 0 };//灰度直方图
		}
	}

	//统计图片中各个灰度值所占的比例
	int nPixelSum = src.rows * src.cols;//总像素值
	float fPixelPct[nGrayScale] = { 0 };//各个灰度值占总体的比例

	for (int i = 0; i < nGrayScale; ++i)
	{
		fPixelPct[i] = 1.0 *nPixelCount[i] / nPixelSum;
	}


	double w0, w1;//背景/目标像素占比
	double u0, u1;//目标/背景平均灰度值
	double fTempVar = 0;//类间方差
	double fMaxVar = 0;//最大类间方差
	double fBestValue = 0;//最优阈值
	double fTemp0, fTemp1;

	for (int k = 0; k < nGrayScale; ++k)
	{
		w0 = w1 = u0 = u1 = fTempVar = 0;
		fTemp0 = fTemp1 = 0;
		//前景,背景区分 [0-k][k+1-255]
		for (int i = 0; i < nGrayScale; ++i)
		{
			//如果当前像素值小于阈值k则属于背景,反之属于目标
			if (i <= k)
			{
				//计算背景像素占比
				w0 += fPixelPct[i];
				//计算当前灰度值发生的概率:灰度值*灰度值发生的概率
				fTemp0 += (i * fPixelPct[i]);

			}
			else
			{
				//计算背景像素占比
				w1 += fPixelPct[i];
				fTemp1 += (i * fPixelPct[i]);
			}
		}
		//计算平均灰度值:p0/w0
		u0 = fTemp0 / w0;
		u1 = fTemp1 / w1;

		//计算类内方差
		fTempVar = (float)(w0 * w1 * pow((u0 - u1), 2));
		if (fTempVar > fMaxVar)
		{
			fMaxVar = fTempVar;
			fBestValue = k;

		}
	}
	
	threshold(src, dst, fBestValue, 255, THRESH_BINARY);
  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值