【拜小白opencv】25-综合示例——8种阈值化操作【OTSU阈值化、自适应阈值化、二进制阈值化、反二进制阈值化、截断阈值化、阈值化为0、反阈值化为0,双阈值化】

常言道“温故而知新”,写此文章就是对自己目前学习内容的小小的总结与记录。

本文力求用最简洁的语言,详细的代码将此部分内容讲解清楚,但由于博主同样是刚刚接触OpenCV,或许表达上有些瑕疵,还望读者能够指教探讨,大家共同进步。

博主机器配置为:VS2013+opencv2.4.13+Win-64bit。

若本文能给读者带来一点点启示与帮助,我就很开心了。

===========================分割线========================


结合前几节的内容,写了个综合示例,包含8种阈值化方法

  1. 二进制阈值化
  2. 反二进制阈值化
  3. 截断阈值化
  4. 阈值化为0
  5. 反阈值化为0
  6. OTSU阈值化
  7. 自适应阈值化
  8. 双阈值化
其中阈值量可通过滑动条来调节,下面来看看程序是如何实现的。
=====================分割线========================

代码演示

/*
	综合示例,8种阈值化操作:
		1.二进制阈值化		2.反二进制阈值化
		3.截断阈值化		4.阈值化为0
		5.反阈值化为0		6.OTSU阈值化
		7.自适应阈值化		8.双阈值化
*/

#include                 
#include                 
#include                
#include               
using namespace std;
using namespace cv;

//------------------------【宏定义部分】------------------------
#define WINDOW_NAME "【几种阈值化操作】"

//-----------------------【全局变量声明部分】------------------------
Mat g_srcImage, g_grayImage, g_dstImage;//源图像、灰度图、结果输出图
Mat g_dstTempImage1, g_dstTempImage2;//双阈值化部分图像
const int g_maxVal = 255; //预设最大值  
int g_nThresholdValue = 90;//初始阈值量
int g_nTresholdType = 0;//阈值类型,并设初值为0
int g_nLowThreshold = 60;//双阈值操作中的较小阈值量
int g_nHighThreshold = 120;//双阈值操作中较大阈值量

//-----------------------【全局函数声明部分】------------------------
static void ShowHelpText();	//显示界面帮助文字
void on_Threshold(int, void*);	//滑动条回调函数
int OTSU(Mat grayImage);    //OTSU法函数实现,返回一个阈值量
Mat otsuThresh();//otsu阈值
Mat adapThresh();//自适应阈值
Mat doubleThresh();//双阈值

//---------------【主函数】-------------------
int main()
{
	ShowHelpText();
	//------------【1】读取源图像并检查图像是否读取成功------------  
	g_srcImage = imread("D:\\OutPutResult\\ImageTest\\tower.jpg");
	if (!g_srcImage.data)
	{
		cout << "读取图片错误,请重新输入正确路径!\n";
		system("pause");
		return -1;
	}
	imshow("【源图像】", g_srcImage);
	//--------------【2】灰度转换--------------
	cvtColor(g_srcImage, g_grayImage, COLOR_RGB2GRAY);
	//--------------【3】创建滑动条来控制阈值---------------
	namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
	createTrackbar("阈值类型:", WINDOW_NAME, &g_nTresholdType, 7, on_Threshold);
	createTrackbar("阈值量:", WINDOW_NAME, &g_nThresholdValue, g_maxVal, on_Threshold);
	createTrackbar("小阈值量:", WINDOW_NAME, &g_nLowThreshold, g_maxVal, on_Threshold);
	createTrackbar("大阈值量:", WINDOW_NAME, &g_nHighThreshold, g_maxVal, on_Threshold);
	//--------------【4】结果在回调函数中显示---------------
	on_Threshold(0, 0);
	//-----------【5】轮询等待用户按键,如果ESC键按下则程序退出---------------
	while (1)
	{
		char c = waitKey(10);     
        if (c == 27)    //按左上角ESC键退出。 27为ESC的ASCII码  
            break;  
	}
	return 0;
}

//-------------------【滑动条的回调函数】---------------------
void on_Threshold(int, void*)
{
	switch (g_nTresholdType)
	{
	case 0:
		cout << "\n当前为【二进制阈值化】操作" << endl;
		threshold(g_grayImage, g_dstImage, g_nThresholdValue, 255, 0);
		imshow(WINDOW_NAME, g_dstImage); break;
	case 1:
		cout << "\n当前为【反二进制阈值化】操作" << endl;
		threshold(g_grayImage, g_dstImage, g_nThresholdValue, 255, 1);
		imshow(WINDOW_NAME, g_dstImage); break;
	case 2:
		cout << "\n当前为【截断阈值化】操作" << endl;
		threshold(g_grayImage, g_dstImage, g_nThresholdValue, 255, 2);
		imshow(WINDOW_NAME, g_dstImage); break;
	case 3:
		cout << "\n当前为【阈值化为0】操作" << endl;
		threshold(g_grayImage, g_dstImage, g_nThresholdValue, 255, 3);
		imshow(WINDOW_NAME, g_dstImage); break;
	case 4:
		cout << "\n当前为【反阈值化为0】操作" << endl;
		threshold(g_grayImage, g_dstImage, g_nThresholdValue, 255, 4);
		imshow(WINDOW_NAME, g_dstImage); break;
	case 5:
		cout << "\n当前为【OTSU阈值化】操作,阈值量不可调" << endl;
		g_dstImage = otsuThresh();
		imshow(WINDOW_NAME, g_dstImage); break;
	case 6:
		cout << "\n当前为【自适应阈值化】操作,阈值量不可调" << endl;
		g_dstImage = adapThresh();
		imshow(WINDOW_NAME, g_dstImage); break;
	case 7:
		cout << "\n当前为【双阈值化】操作,请分别调节大、小阈值量" << endl;
		g_dstImage = doubleThresh();
		imshow(WINDOW_NAME, g_dstImage); break;
	}
}

//---------------【OTSU法函数实现】----------------------  
//		功能:仅仅返回一个阈值量,还未实现阈值化
//-------------------------------------------------------
int OTSU(Mat grayImage)
{
	int nRows = grayImage.rows;//行数  
	int nCols = grayImage.cols;//列数   
	int threshold = 0;  //初始化阈值  
	//------------【1】初始化统计参数------------  
	int nSumPix[256];   //用于灰度级中每个像素在整幅图像中的个数  
	float nProDis[256]; //用于记录每个灰度级占图像中的概率分布  
	for (int i = 0; i < 256; i++)
	{
		nSumPix[i] = 0;
		nProDis[i] = 0;
	}
	//------------【2】统计灰度级中每个像素在整幅图像中的个数------------  
	for (int i = 0; i < nRows; i++)
	{
		for (int j = 0; j < nCols; j++)
		{
			nSumPix[(int)grayImage.at(i, j)]++;
		}
	}
	//------------【3】计算每个灰度级占图像中的概率分布------------  
	for (int i = 0; i < 256; i++)
	{
		nProDis[i] = (float)nSumPix[i] / (nCols*nRows);
	}
	//------------【4】遍历灰度级[0,255],计算出最大类间方差的阈值------------  
	float wb, wf; //比重. wb-背景部分; wf-前景部分  
	float u0_temp, u1_temp, u0, u1; //平均值  
	float delta_temp;   //存放临时方差  
	double delta_max = 0.0; //初始化最大类间方差  
	for (int i = 0; i < 256; i++)
	{
		wb = wf = u0_temp = u1_temp = u0 = u1 = delta_temp = 0;//初始化相关参数  
		for (int j = 0; j < 256; j++)
		{
			//背景部分  
			if (j <= i)
			{
				//当前i为分割阈值,第一类总的概率  
				wb += nProDis[j];   //比重  
				u0_temp += j*nProDis[j];
			}
			//前景部分  
			else
			{
				//当前i为分割阈值,第一类总的概率  
				wf += nProDis[i];   //比重  
				u1_temp += j*nProDis[j];
			}
		}
		//------------分别计算各类的平均值------------  
		u0 = u0_temp / wb;
		u1 = u1_temp / wf;
		//-----------计算最大类间方差------------  
		delta_temp = (float)(wb*wf*pow((u0 - u1), 2));//形如pow(x,y);其作用是计算x的y次方。  
		//------------依次找到最大类间方差下的阈值------------  
		if (delta_temp > delta_max)
		{
			delta_max = delta_temp;
			threshold = i;
		}
	}//计算结束  
	return threshold;   //返回OTUS计算出的阈值  
}

//---------------【OTSU阈值化函数实现】----------------------  
Mat otsuThresh()
{
	int otsuThreshold = OTSU(g_grayImage);	//调用OTSU()阈值化所得到阈值
	cout << "--------且调用OTSU()阈值化所得阈值量为:" << otsuThreshold << endl;
	g_dstImage = Mat::zeros(g_grayImage.rows, g_grayImage.cols, CV_8UC1);//创建输出结果图像  
	//------------【利用得到的阈值实现二值化操作】------------  
	for (int i = 0; i < g_grayImage.rows; i++)
	{
		for (int j = 0; j < g_grayImage.cols; j++)
		{
			//阈值判断。 若该点值大于阈值,则设为255,否则设为0。  
			if (g_grayImage.at(i, j) > otsuThreshold)
				g_dstImage.at(i, j) = 255;
			else
				g_dstImage.at(i, j) = 0;
		}
	}
	return g_dstImage;
}

//---------------【自适应阈值化函数实现】----------------------  
Mat adapThresh()
{
	//------------【初始化相关变量】---------------    
	int blockSize = 3;  //邻域块大小。取值3、5、7....等  
	int constValue = 10;
	int adaptiveMethod = 0;	//自适应阈值算法
	int thresholdType = 1;	//阈值类型
	/*
		自适应阈值算法
		0:ADAPTIVE_THRESH_MEAN_C
		1:ADAPTIVE_THRESH_GAUSSIAN_C
		--------------------------------------
		阈值类型
		0:THRESH_BINARY
		1:THRESH_BINARY_INV
	*/
	//---------------【图像自适应阈值操作】-------------------------  
	adaptiveThreshold(g_grayImage, g_dstImage, g_maxVal, adaptiveMethod, thresholdType, blockSize, constValue);
	return g_dstImage;
}

//---------------【双边阈值化函数实现】----------------------  
Mat doubleThresh()
{
	//小阈值对源灰度图像进行二进制阈值化操作  
	threshold(g_grayImage, g_dstTempImage1, g_nLowThreshold, g_maxVal, THRESH_BINARY);
	//大阈值对源灰度图像进行反二进制阈值化操作  
	threshold(g_grayImage, g_dstTempImage2, g_nHighThreshold, g_maxVal, THRESH_BINARY_INV);
	//矩阵"与运算"得到二值化结果  
	bitwise_and(g_dstTempImage1, g_dstTempImage2, g_dstImage); //对像素加和  
	return g_dstImage;
}

//---------------【显示界面帮助文字】----------------------  
void ShowHelpText() 
{
	cout << "\t按键操作说明:" << endl << endl;
	cout << "\t\t键盘按键【Esc】-退出程序" << endl;
	cout << "\t\t阈值类型0- 二进制阈值化" << endl;
	cout << "\t\t阈值类型1- 反二进制阈值化" << endl;
	cout << "\t\t阈值类型2- 截断阈值化" << endl;
	cout << "\t\t阈值类型3- 阈值化为0" << endl;
	cout << "\t\t阈值类型4- 反阈值化为0" << endl;
	cout << "\t\t阈值类型5- OTSU阈值化" << endl;
	cout << "\t\t阈值类型6- 自适应阈值化" << endl;
	cout << "\t\t阈值类型7- 双阈值化" << endl;
	cout << "-------------------------------------------------" << endl << endl;
}


========================分割线======================

显示结果

【拜小白opencv】25-综合示例——8种阈值化操作【OTSU阈值化、自适应阈值化、二进制阈值化、反二进制阈值化、截断阈值化、阈值化为0、反阈值化为0,双阈值化】_第1张图片

【拜小白opencv】25-综合示例——8种阈值化操作【OTSU阈值化、自适应阈值化、二进制阈值化、反二进制阈值化、截断阈值化、阈值化为0、反阈值化为0,双阈值化】_第2张图片

【拜小白opencv】25-综合示例——8种阈值化操作【OTSU阈值化、自适应阈值化、二进制阈值化、反二进制阈值化、截断阈值化、阈值化为0、反阈值化为0,双阈值化】_第3张图片

【拜小白opencv】25-综合示例——8种阈值化操作【OTSU阈值化、自适应阈值化、二进制阈值化、反二进制阈值化、截断阈值化、阈值化为0、反阈值化为0,双阈值化】_第4张图片

【拜小白opencv】25-综合示例——8种阈值化操作【OTSU阈值化、自适应阈值化、二进制阈值化、反二进制阈值化、截断阈值化、阈值化为0、反阈值化为0,双阈值化】_第5张图片

【拜小白opencv】25-综合示例——8种阈值化操作【OTSU阈值化、自适应阈值化、二进制阈值化、反二进制阈值化、截断阈值化、阈值化为0、反阈值化为0,双阈值化】_第6张图片

【拜小白opencv】25-综合示例——8种阈值化操作【OTSU阈值化、自适应阈值化、二进制阈值化、反二进制阈值化、截断阈值化、阈值化为0、反阈值化为0,双阈值化】_第7张图片

【拜小白opencv】25-综合示例——8种阈值化操作【OTSU阈值化、自适应阈值化、二进制阈值化、反二进制阈值化、截断阈值化、阈值化为0、反阈值化为0,双阈值化】_第8张图片

【拜小白opencv】25-综合示例——8种阈值化操作【OTSU阈值化、自适应阈值化、二进制阈值化、反二进制阈值化、截断阈值化、阈值化为0、反阈值化为0,双阈值化】_第9张图片

======================分割线======================

程序说明

其中阈值类型0--4,通过右上侧的阈值量来调节。
阈值类型5和6,为计算所得阈值量,不能调节。
阈值类型7,通过下面的,大、小阈值量来调节。

=====================END=====================

你可能感兴趣的:(【拜小白OpenCV】,一步步学习OpenCV2)