利用OpenCV实现OTSU分割和最大熵分割

Otsu分割

一、 原理

我们利用otsu分割算法可以对一张图片的前景和背景进行分割。其思想主要是寻找前景与背景之间的界限灰度值(大于该界限灰度值的部分为前景,小于),前景的平均灰度值减去图像的平均灰度值的平方记为k0,背景的平均灰度值减去图像的平均灰度值的平方记为k1,此时将前景所占的图像百分比m0乘以k0在加上背景所占的百分比m1乘以k1,记相加和为s。这里的关键是选定界定背景和前景的界定灰度值,我们并不知道这个灰度值是多少,所以我们需要一个一个灰度值的试,试出来最后选取s最大的即为真正的界定灰度值。

二、 代码实现

//通过OTSU算法来确定阈值
//该算法需要的参数有:前景(背景)像素比例;前景/背景平均灰度值;类间方差公式:g=w0*w0*(u0-u1)(u0-u1)
void OTSU(Mat srcImage) {
	int cols = srcImage.cols;
	int rows = srcImage.rows;
	int thresh = 0;
	Mat GrayImage;
	cvtColor(srcImage, GrayImage, COLOR_BGR2GRAY);
	//初始化统计参数
	int sumpix[256];
	float prodis[256];
	for (int i = 0; i < 256; i++) {
		sumpix[i] = 0;
		prodis[i] = 0;
	}
	//统计灰度级中每个像素在整幅图像中的个数
	for (int i = 0; i < rows; i++) {
		for (int j = 0; j < cols; j++) {
			sumpix[(int)GrayImage.at<uchar>(i, j)]++;
		}
	}
	//计算每个灰度级占图像中的概率分布
	for (int i = 0; i < 256; i++) {
		prodis[i] = (float)sumpix[i] / (cols*rows);
	}
	//遍历灰度级0到255,计算出最大类间方差下的阈值
	float w0, w1, u0_0, u1_1, u0, u1, g;
	double maxg = 0;
	for (int i = 0; i < 256; i++) {
		//每次寻找可能的最大阈值之前需要先对相关参数初始化
		w0 = w1 = u0_0 = u1_1 = u0 = u1 = g = 0;
		for (int j = 0; j < 256; j++) {
			//这里是指的背景
			if (j <= i) {
				//当这时候的i为阈值时,第一类的总概率,以及第一类中每个灰度级的加权值(方便后面计算出平均灰度级)
				w0 += prodis[j];
				u0_0 += j * prodis[j];
			}
			else {
				//第二类
				w1 += prodis[j];
				u1_1 += j * prodis[j];
			}
		}
		//分别计算各类的平均灰度,利用公式u0*w0=u0_0;
		u0 = u0_0 / w0;
		u1 = u1_1 / w1;
		g = (float)(w0*w1*pow((u0 - u1), 2));
		//依次比较找到最大类间方差的阈值
		if (g > maxg) {
			maxg = g;
			thresh = i;
		}
	}
	MaxThresh = thresh;
	Mat destImage;
	threshold(GrayImage, destImage, thresh, 255, THRESH_BINARY);
	imshow("OTSU二值化结果图", destImage);
	
}

最大熵阈值分割

一、 原理

参考上面的otsu的划分前景背景方法,这里我们也需要一种界定手段,来寻找我们期待的阈值,从而求得最大熵。

给定了一个特定的阈值q(即某一灰度值),根据该阈值将图片划分为背景和前景。分别将背景和前景中的每一个灰度级的占图片的概率进行累加,结果记为P0和P1。则背景和前景分别对应的最大熵为:

opencv剖分 opencv分割算法_算法


H0+H1当达到最大时,我们就说 这时候的阈值为分界灰度值,大于此灰度值的为前景,小于的则为背景。

二、 代码实现

//最大熵阈值分割
//该算法所需参数有:前景/背景每个灰度级所占的比例;前景/背景所有灰度级概率总和;计算前景/背景的熵
void Shang(Mat srcImage) {
	int cols = srcImage.cols;
	int rows = srcImage.rows;
	int thresh = 0;
	Mat GrayImage;
	cvtColor(srcImage, GrayImage, COLOR_BGR2GRAY);
	//初始化统计参数
	int sumpix[256];
	float prodis[256];
	for (int i = 0; i < 256; i++) {
		sumpix[i] = 0;
		prodis[i] = 0;
	}
	//统计灰度级中每个像素在整幅图像中的个数
	for (int i = 0; i < rows; i++) {
		for (int j = 0; j < cols; j++) {
			sumpix[(int)GrayImage.at<uchar>(i, j)]++;
		}
	}
	//计算每个灰度级占图像中的概率分布
	for (int i = 0; i < 256; i++) {
		prodis[i] = (float)sumpix[i] / (cols*rows);
	}
	//遍历灰度级0到255,计算
	float P0, P1, H0, H1, H;
	double maxH = 0;
	for (int i = 0; i < 256; i++) {
		//每次寻找可能的最大阈值之前需要先对相关参数初始化
		P0 = P1 = H0 = H1 = H =0;
		for (int j = 0; j < 256; j++) {
			//这里是指的背景
			if (j < i) {
				//当这时候的i为阈值时,第一类的总概率
				P0 += prodis[j];
			}
			else {
				//第二类
				P1 += prodis[j];
			}
		}
		for (int j = 0; j < 256; j++) {
			//这里是指的背景
			if (j < i) {
				//当这时候的i为阈值时,计算第一类累计概率
				if (prodis[j] == 0) continue;
				float bizhi0 = prodis[j] / P0;
				H0 += -bizhi0 * logf(bizhi0);
			}
			else {
				//第二类
				if (prodis[j] == 0) continue;
				float bizhi1 = prodis[j] / P1;
				H0 += -bizhi1 * logf(bizhi1);
			}
		}
		H = H0 + H1;
		//依次比较找到最大类间方差的阈值
		if (H > maxH) {
			maxH = H;
			thresh = i;
		}
	}
	cout << "thresh=" << thresh << endl;
	MaxThresh = thresh;
	Mat destImage;
	threshold(GrayImage, destImage, thresh, 255, THRESH_BINARY);
	imshow("熵二值化结果图", destImage);

}