C++与opencv实现OTSU自适应阈值分割算法

OTSU算法原理

Otsu算法(大津法或最大类间方差法)使用的是聚类的思想。它把图像的灰度数按灰度级分成2个部分,使得两个部分之间的灰度值差异最大,每个部分之间的灰度差异最小。
通过方差的计算来寻找一个合适的灰度级别来划分。 所以可以在二值化的时候采用otsu算法来自动选取阈值进行二值化。
otsu算法被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。因此,使类间方差最大的分割意味着错分概率最小。
设t为设定的阈值。

w0: 分开后前景像素点数占图像的比例
u0: 分开后前景像素点的平均灰度
w1: 分开后背景像素点数占图像的比例
u1: 分开后背景像素点的平均灰度

图像总平均灰度为: u = w0∗u0 + w1∗u1

从L(灰度图为0-255)个灰度级遍历 t,使得 t 为某个值的时候,前景和背景的方差最大,则 这个 t 值便是我们要求得的阈值。其中,方差的计算公式如下:

g = wo∗(u0−u)∗(u0−u) + w1∗(u1−u)∗(u1−u)

此公式计算量较大,可以采用:

g = w0∗w1∗(u0−u1)∗(u0−u1)

由于Otsu算法是对图像的灰度级进行聚类,因此在执行Otsu算法之前,需要计算该图像的灰度直方图。

C++/C代码实现

int avg256cal::getOTSUthread(Mat& src)
{
	int size = 256;

	int *NoArry = new int[size];// 直方图矩阵

	for (int i = 0; i < size; ++i)// 初始化
		NoArry[i] = 0;

	int r = src.rows;
	int c = src.cols;
	int sum = r*c;

//  建立直方图矩阵
	for (int i = 0; i < r; ++i)
	{
		for (int j = 0; j < c; ++j)
		{	
			uchar pix = src.at<uchar>(i, j);
			NoArry[pix]++;
		}
	}
	//delete[] NoArry;

	int thd = 0; // 阈值
	float w1 = 0, w2 = 0, u1 = 0, u2 = 0, u = 0, thg = 0, MaxTh = 0;
	int cnt1 = 0, cnt2 = 0;
	for (int i = 1; i <= 255; ++i)
	{
		u1 = u2 = w1 = w2 = 0; // 均值,比例初始化
		cnt1 = 0;cnt2 = 0;
		int wt1 = 0, wt2 = 0;// weight初始化

		for (int j = 0; j <i; ++j)
		{
			cnt1 += NoArry[j];
			wt1 += j*NoArry[j];
		}
		u1 = wt1 / cnt1;
		w1 = cnt1*1.0 / sum;

		for (int j = i; j < 256; ++j)
		{
			cnt2 += NoArry[j];
			wt2 += j*NoArry[j];
		}
		u2 = wt2 / cnt2;
		w2 = cnt2*1.0 / sum;

		thg = w1*w2*(u1 - u2)*(u1 - u2);
		if (MaxTh < thg)// 找最大类间方差阈值
		{
			MaxTh = thg;
			thd = i;
		}
	}

	return thd;
}
发布了56 篇原创文章 · 获赞 10 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/birenxiaofeigg/article/details/98481019