数字图像处理(十)图像分割之全局阈值处理、otsu阈值处理、多阈值处理、可变阈值处理、多变量阈值处理

本篇所有代码都是基于24位BMP图像

一. 基本的全局阈值处理

当图像的前景和背景相关的只方图之间存在一个相当清晰的波谷时,这个算法效果很好。这部分算法比较简单,由于时间关系,并没有写相关代码

算法步骤:

一、为全局阈值T选择一个初始的估计值(一般选平均灰度)

二、用T分割该图像产生的像素组,G1为灰度值大于T的所有像素组成,G2为灰度值小于T的所有像素组成

三、对G1和G2分别计算平均灰度值m1和m2,计算一个新的阈值T=(m1+m2)/2

四、重复步骤二、步骤三,知道连续迭代中T值之间的差小于一个预定义的△T值为止。

二. otsu(最佳阈值处理也叫最大类间差法)

最大类间差法是一种使用类间方差最大的自动确定阈值的方法。

算法步骤:

一、首先直方图统计Pi,C1为灰度值小于T的所有像素组成,C2为灰度值大于T的所有像素组成

二、令阈值T从0-255开始,遍历计算C1组和C2组的平均灰度m1和m2,以及全局均值mG,求类间方差的最大值

分配到c1像素的平均灰度m1 = mk/p1;

K级累加均值即T=k时,mk = 求和(i*Pi) i = 0,1,2,...,k

全局均值 mG = 求和(i*Pi) i = 0,1,2...L-1

C1发生的概率 P1 = 求和(Pi) i = 0,1,2,...,k

C2发生的概率 P2 = 1-P1

因为 P1*m1 + P2*m2 = mG,所以m2 = (mG-P1*m1)/P2

而类间方差 = P1(m1 - mG)*(m1 - mG) + P2(m2 - mG)*(m2 - mG)

       = P1*P2*(m1-m2)*(m1-m2)

所以,最终变量只需要求mk,P1.全局均值mG是常量,一开始就能算出来

三、阈值确定后,小于等于K的为0,大于K的为255.

代码如下

MFC建立类向导

void CImageProcessingView::OnTxfgOtsu()
{
	// TODO: 在此添加命令处理程序代码
	if (numPicture == 0)
	{
		AfxMessageBox("请输入一张图片", MB_OK, 0);
		return;
	}
	if (m_nBitCount != 24)
	{
		AfxMessageBox("输入图片不是24位", MB_OK, 0);
		return;
	}
	AfxMessageBox("图像分割,otsu全局阈值分割", MB_OK, 0);

	int num;//记录每一行需要填充的字节  
	if (m_nWidth * 3 % 4 != 0)
	{
		num = 4 - m_nWidth * 3 % 4;
	}
	else
	{
		num = 0;
	}

	//打开临时的图片    
	FILE *fpo = fopen(BmpName, "rb");
	FILE *fpw = fopen(BmpNameLin, "wb+");
	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
	fread(m_pImage, m_nImage, 1, fpo);
	unsigned char *ImageSize;
	ImageSize = new unsigned char[m_nImage];


	//统计直方图
	//这个代码是基于24位真彩色图像,有三个通道,这里我只以统计一个通道。
	int hist[256] = { 0 };//存放每个灰度的数量
	double pro_hist[256] = { 0.0 };// 存放每个灰度数量占所有像素数量的概率
	int x,y;//对应X轴和Y轴
	int temp;//对应的灰度值
	int i,j;

	//统计每个灰度的数量
	for (y = 0; y < m_nHeight; y++)
	{
		for (x = 0; x < m_nWidth; x++)
		{
			temp = m_pImage[(x + y*m_nWidth) * 3 + y*num];
			hist[temp]++;
		}
	}

	//统计每个灰度所占的概率
	for (i = 0; i < 256; i++)
	{
		pro_hist[i] = 1.0 * hist[i] / (m_nWidth*m_nHeight);
	}

	//计算平均灰度值
	double mG = 0.0,m1 = 0.0,m2 = 0.0,p1 = 0.0,p2 = 0.0,mk;

	for (i = 0; i++; i < 256)
	{
		mG += i * pro_hist[i];
	}

	//计算前景和背景的类间方差,并得出最大阈值
	double maxc=0.0,c;//定义类间方差
	int maxT=0;//最大阈值
	for (i = 0; i < 256; i++)
	{
		m1 = 0.0, m2 = 0.0, p1 = 0.0, p2 = 0.0,mk = 0.0;//每一次循环归0//一般只需要计算p1和mk。
		for (j = 0; j < i; j++)
			{
				p1 += pro_hist[i];
				mk += i*pro_hist[i];
			}
		m1 = mk / p1;	//mk = m1*p1
		p2 = 1 - p1;
		m2 = (mG - mk) / p2;
		
		c = p1*p2*(m1 - m2)*(m1 - m2);
		if (c > maxc)
		{
			maxc = c;
			maxT = i;
		}
	}

	//最佳阈值得到后,对图像进行阈值处理,小于等于maxT的为0,大于maxT的为255
	for (y = 0; y < m_nHeight; y++)
	{
		for (x = 0; x < m_nWidth; x++)
		{
			if (m_pImage[(x + y*m_nWidth) * 3 + y*num] > maxT)
			{
				ImageSize[(x + y*m_nWidth) * 3 + y*num] = 255;
				ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = 255;
				ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = 255;
			}
				
			else
			{
				ImageSize[(x + y*m_nWidth) * 3 + y*num] = 0;
				ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = 0;
				ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = 0;
			}
		}
	}

	fwrite(ImageSize, m_nImage, 1, fpw);

	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level = 400;
	Invalidate();

}

效果如下


三. 多阈值处理

这里简单提一下多阈值otsu。其实和单阈值思想差不多,以双阈值为例,目的就是找到两个阈值T1和T2将图像小于等于T1,大于T1但小于等于T2,大于T2三部分

一、首先直方图统计Pi,C1为灰度值小于等于T1的所有像素组成,C2为灰度值大于T1小于等于T2的所有像素组成

C3为大于T2像素组成

二、令阈值k1从1-255开始,阈值k2从K1-255,循环遍历计算C1、C2、C3组的平均灰度m1、m2和m3,以及全局均值mG,求类间方差的最大值

分配到c1像素的平均灰度 m1 = mk1/P1;

分配到c2像素的平均灰度 m2 = mk2/P2;

分配到c3像素的平均灰度 m3 = mk3/P3

K级累加均值即T=k时,mk = 求和(i*Pi) i = 0,1,2,...,k

全局均值 mG = 求和(i*Pi) i = 0,1,2...L-1

C1发生的概率 P1 = 求和(Pi) i = 0,1,2,...,k

有 P1 + P2 + P3=1;

而类间方差 = P1(m1 - mG)*(m1 - mG) + P2(m2 - mG)*(m2 - mG)+ P3(m3-mG)*(m3-mG)


所以,求k1和k2使类间方差最大

三、阈值确定后,将图像分成 小于等于T1,大于T1但小于等于T2,大于T2三部分

四. 可变阈值处理(自适应阈值分割)

我理解成将图像分成N个小窗口,对每个窗口设置阈值,一种比较简单的方法就是寻找窗口内的最大值和最小值,选取均值作为阈值。当然,也可以每个窗口用otsu。

五. 多变量阈值处理

之前讨论的都是基于灰度的,但有的情况下会有多个变量,如彩色图像R、G、B.

一、假设a是我们感兴趣的平均微红色,一种用来分割彩色图像的方法是计算任意彩色点a 的距离度量

D(z,a),为了表达方便,我记为D。则图像小于D的为1,大于D的为0;

欧氏距离:D=||z-|| = sqrt((z-a)T * z-a),z-a)T为转置

还有一种更有用的距离测量,马氏距离

D = sqrt(z-a)T * C(-1)z-a),C(-1)为C的逆矩阵,C是Z的协方差矩阵

猜你喜欢

转载自blog.csdn.net/hjxu2016/article/details/80766949