灰度化与二值化
1、灰度化
对于单通道灰度图像,其某个位置处的像素点灰度值可由同位置处的三通道RGB值计算得到,公式如下:
实现代码:
/*****************************************************************************
函数名 : ImgRGBToGray
功能 : RGB彩色转灰度图像
算法实现 : <可选项>
参数说明 : tSrcImg 原图像[in]
ptDstImg 目标图像[out]
返回值说明 : 无
其他说明 : 无
******************************************************************************/
void ImgRGBToGray(MyImage tSrcImg, MyImage *ptDstImg)
{
l32 l32SrcImgHeight = tSrcImg.l32ImgH;
l32 l32SrcImgWidth = tSrcImg.l32ImgW;
l32 l32DstImgHeight = l32SrcImgHeight;
l32 l32DstImgWidth = l32SrcImgWidth;
MyImageInit(ptDstImg, l32DstImgHeight, l32DstImgWidth, 1);
for (l32 l32DstImgHeightIdx = 0; l32DstImgHeightIdx < l32DstImgHeight; l32DstImgHeightIdx++)
{
for (l32 l32DstImgWidthIdx = 0; l32DstImgWidthIdx < l32DstImgWidth; l32DstImgWidthIdx++)
{
l32 l32DstImgPixelPos = l32DstImgHeightIdx * l32DstImgWidth + l32DstImgWidthIdx;
l32 l32SrcImgPixelBPos = l32DstImgHeightIdx * l32DstImgWidth * 3 + \
l32DstImgWidthIdx * 3 + 0;
l32 l32SrcImgPixelGPos = l32DstImgHeightIdx * l32DstImgWidth * 3 + \
l32DstImgWidthIdx * 3 + 1;
l32 l32SrcImgPixelRPos = l32DstImgHeightIdx * l32DstImgWidth * 3 + \
l32DstImgWidthIdx * 3 + 2;
ptDstImg->pu8Data[l32DstImgPixelPos] = tSrcImg.pu8Data[l32SrcImgPixelBPos] * 0.114 + tSrcImg.pu8Data[l32SrcImgPixelGPos] * 0.587 \
+ tSrcImg.pu8Data[l32SrcImgPixelRPos] * 0.229;
}
}
}
二、二值化
在灰度图像中,对于一确定的阈值T有:
实现代码:
/*****************************************************************************
函数名 : ImgGrayToBina
功能 : 灰度图像二值化
算法实现 : <可选项>
参数说明 : tSrcImg 原图像[in]
ptDstImg 目标图像[out]
u8Thresh 阈值
返回值说明 : 无
其他说明 : 无
******************************************************************************/
void ImgGrayToBina(MyImage tSrcImg, MyImage *ptDstImg, u8 u8Thresh)
{
l32 l32SrcImgHeight = tSrcImg.l32ImgH;
l32 l32SrcImgWidth = tSrcImg.l32ImgW;
l32 l32DstImgHeight = l32SrcImgHeight;
l32 l32DstImgWidth = l32SrcImgWidth;
MyImageInit(ptDstImg, l32DstImgHeight, l32DstImgWidth, 1);
for (l32 l32DstImgHeightIdx = 0; l32DstImgHeightIdx < l32DstImgHeight; l32DstImgHeightIdx++)
{
for (l32 l32DstImgWidthIdx = 0; l32DstImgWidthIdx < l32DstImgWidth; l32DstImgWidthIdx++)
{
l32 l32DstImgPixelPos = l32DstImgHeightIdx * l32DstImgWidth + l32DstImgWidthIdx; //目标图像像素在内存中的位置
l32 l32SrcImgPixelPos = l32DstImgPixelPos;
if (tSrcImg.pu8Data[l32SrcImgPixelPos] > u8Thresh)
{
ptDstImg->pu8Data[l32DstImgPixelPos] = 255;
}
else
{
ptDstImg->pu8Data[l32DstImgPixelPos] = 0;
}
}
}
}
三、OSTU最佳阈值(大津法)
对于一幅灰度图像,假设阈值t将其划分为前景和背景两部分A和B。设A为前景,B为背景,前景像素数占总像素数的比例PA,背景像素数占总像素数比例PB,前景与背景的平均灰度值分别为MA和MB,总平均灰度值为M。那么Nobuyuki Otsu 给出的类间方差定义为:
而最佳阈值t即为使得方差ICV最大的阈值T。
程序设计思路:
遍历原灰度图像中的每个像素点,计算平均灰度值M与灰度直方图H,H用256个元素的数组表示,其中H[i]表示灰度值为i的像素点个数。然后,遍历每个灰度值0~255,分别计算PA、MA、PB、MB并计算ICV,找出使得ICV最大的阈值T。
实现代码:
/*****************************************************************************
函数名 : ImgGetThreshOstu
功能 : OSTU阈值计算
算法实现 : <可选项>
参数说明 : tSrcImg 原图像[in]
返回值说明 : u8BestThresh 自适应阈值[out]
其他说明 : 无
******************************************************************************/
u8 ImgGetThreshOstu(MyImage tSrcImg)
{
l32 l32SrcImgHeight = tSrcImg.l32ImgH;
l32 l32SrcImgWidth = tSrcImg.l32ImgW;
l32 l32TotalPixelNums = l32SrcImgHeight * l32SrcImgWidth;
l32 l32TotalPixelValue = 0;//总灰度值
f32 f32MeanPixelValue;//灰度均值
l32 al32GrayValueCnt[256] = { 0 };//统计灰度值数
for (l32 l32SrcImgHeightIdx = 0; l32SrcImgHeightIdx < l32SrcImgHeight; l32SrcImgHeightIdx++)
{
for (l32 l32SrcImgWidthIdx = 0; l32SrcImgWidthIdx < l32SrcImgWidth; l32SrcImgWidthIdx++)
{
l32 l32SrcImgPixelPos = l32SrcImgHeightIdx * l32SrcImgWidth + l32SrcImgWidthIdx; //第一个通道像素在内存中的位置
l32TotalPixelValue += tSrcImg.pu8Data[l32SrcImgPixelPos];//统计总灰度值
al32GrayValueCnt[tSrcImg.pu8Data[l32SrcImgPixelPos]] ++;
}
}
f32MeanPixelValue = f32(l32TotalPixelValue) / l32TotalPixelNums;
f32 f32MaxICV = 0;
u8 u8BestThresh = 0;
for (l32 l32ThreshValue = 1; l32ThreshValue <= 255; l32ThreshValue++)
{
l32 l32TotalPixelNumsA = 0, l32TotalPixelNumsB = 0;
l32 l32TotalPixelValueA = 0, l32TotalPixelValueB = 0;
f32 f32PA, f32PB;
f32 f32MA, f32MB;
f32 f32ICV;
for (u8 u8GrayValueIdx = 0; u8GrayValueIdx < l32ThreshValue; u8GrayValueIdx++)
{
l32TotalPixelNumsA += al32GrayValueCnt[u8GrayValueIdx];
l32TotalPixelValueA += u8GrayValueIdx*al32GrayValueCnt[u8GrayValueIdx];
}
f32PA = f32(l32TotalPixelNumsA) / l32TotalPixelNums;
f32PB = 1.0 - f32PA;
l32TotalPixelNumsB = l32TotalPixelNums - l32TotalPixelNumsA;
l32TotalPixelValueB = l32TotalPixelValue - l32TotalPixelValueA;
f32MA = f32(l32TotalPixelValueA) / l32TotalPixelNumsA;
f32MB = f32(l32TotalPixelValueB) / l32TotalPixelNumsB;
f32ICV = f32PA * (f32MA - f32MeanPixelValue) * (f32MA - f32MeanPixelValue) + f32PB * (f32MB - f32MeanPixelValue) * (f32MB - f32MeanPixelValue);
//f32ICV = f32PA * f32PB * (f32MA - f32PB) * (f32MA - f32MB);
if (f32ICV > f32MaxICV)
{
f32MaxICV = f32ICV;
u8BestThresh = u8(l32ThreshValue);
}
}
return u8BestThresh;
}