常言道“温故而知新”,写此文章就是对自己目前学习内容的小小的总结与记录。
本文力求用最简洁的语言,详细的代码将此部分内容讲解清楚,但由于博主同样是刚刚接触OpenCV,或许表达上有些瑕疵,还望读者能够指教探讨,大家共同进步。
博主机器配置为:VS2013+opencv2.4.13+Win-64bit。
若本文能给读者带来一点点启示与帮助,我就很开心了。
===========================分割线========================
结合前几节的内容,写了个综合示例,包含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;
}