1.二值化:
图1 二值化(阈值:140)处理效果
所谓二值化简单一点讲,就是将图像划分成黑和白,通过设定一个标准如果大于这个标准就设为白,如果小于这个标准,就设为黑,而这个标准,就叫做阈值。
具体定义如下所示:
下面给出实现的代码:
//二值化
//函数的参数iTR为阈值
void CBMPSampleDlg::ThresholdProcess(int iTR)
{
//读取BMP文件
m_Dib.AttachMapFile("1.bmp", TRUE);
m_Dib.CopyToMapFile("二值化.bmp");
//将像素指针定位到图像数据的开始
RGBTRIPLE *rgbtri = (RGBTRIPLE *)m_Dib.m_lpImage;
//获得图像的大小
int iSize = m_Dib.GetSizeImage();
//BMP文件头指针
BITMAPINFOHEADER * pBmiHeader = (BITMAPINFOHEADER *)m_Dib.m_lpBMIH;
//遍历每一个像素,并判断每一个像素的分量(RGB),将其与阈值比较,然后进行赋值
for(int i = 0; i < iSize/( pBmiHeader->biBitCount / 8); i++)
{
if ( (rgbtri[i].rgbtRed < iTR )| (rgbtri[i].rgbtGreen < iTR) | (rgbtri[i].rgbtBlue < iTR) )
{
rgbtri[i].rgbtRed = (BYTE) 0;
rgbtri[i].rgbtGreen = (BYTE) 0;
rgbtri[i].rgbtBlue = (BYTE) 0;
}
else
{
rgbtri[i].rgbtRed = (BYTE) 255;
rgbtri[i].rgbtGreen = (BYTE) 255;
rgbtri[i].rgbtBlue = (BYTE) 255;
}
}
//显示图像
DrawPic();
}
在读取图像之后,会将指针定位到图像像素数据的开始位置,然后获得图像的大小,然后通过BMP文件头获得图像的一个像素所占据的二进制的位数,这样就知道一个像素由几个字节组成的了,需要注意的是,一个像素不一定是由三个字节组成的,比如是灰度图像其只需要一个字节来存储一个像素究竟是灰到什么程度其范围在0-255 之间,而彩色图像却是由三种颜色组成的也就是所说的三原色RGB分别为Red、Green、Blue三种颜色组成,这三种颜色每个分量各占一个字节,所以这里需要三个字节,另外在BMP图像中还一个结构为RGBQUAD的结构体,这里一个像素占据的是4个字节,其实,这里就涉及到了8位图像24位图像以及32位图像的问题了,所谓的8位图像其实,每一个像素占一个字节,24位图像,每一个像素占据3个字节、而32位图像每一个像素占据4个字节就是这么来的。代码中,首先会读取原始图像文件,文件的格式为BMP的,关于BMP图像的存储结构,在接下来的文章中会讲到。
2.海报化
图2 海报化处理效果
所谓的海报化其实就是将每一个像素的分量与224进行与运算,而244的16进制表示可以表示成0xe0,前面介绍了一个像素的分量的范围在0-255范围内,所以只需要将这两个数值的二进制位相与即可完成海报化的处理效果。
下面为实现的具体代码:
//海报化
void CBMPSampleDlg::Posterize()
{
m_Dib.AttachMapFile("1.bmp", TRUE);
m_Dib.CopyToMapFile("海报化.bmp");
RGBTRIPLE *rgbtri = (RGBTRIPLE *)m_Dib.m_lpImage;
int iSize = m_Dib.GetSizeImage();
BITMAPINFOHEADER * pBmiHeader = (BITMAPINFOHEADER *)m_Dib.m_lpBMIH;
for(int i = 0; i < iSize/( pBmiHeader->biBitCount / 8); i++)
{
rgbtri[i].rgbtRed = (BYTE) (rgbtri[i].rgbtRed & 0xe0);
rgbtri[i].rgbtGreen = (BYTE) (rgbtri[i].rgbtGreen & 0xe0);
rgbtri[i].rgbtBlue = (BYTE) (rgbtri[i].rgbtBlue & 0xe0);
}
DrawPic();
}
上面的这段代码是参考DirectShow里面的ezrgb24滤镜这个例子改写的,另外下面的灰度化也是采用里面的改写的。
3.灰度化
图3 灰度化处理效果
灰度化有很多种处理方法,有分量法、最大值法、平均值法以及加权平均值法。
1)分量法
将彩色图像中的三分量的亮度作为三个灰度图像的灰度值,可根据应用需要选取一种灰度图像。
f1(i,j)=R(i,j) f2(i,j)=G(i,j)f3(i,j)=B(i,j)
其中fk(i,j)(k=1,2,3)为转换后的灰度图像在(i,j)处的灰度值。
2)最大值法
将彩色图像中的三分量亮度的最大值作为灰度图的灰度值。
f(i,j)=max(R(i,j),G(i,j),B(i,j))
3) 平均值法
将彩色图像中的三分量亮度求平均得到一个灰度图。
f(i,j)=(R(i,j)+G(i,j)+B(i,j)) /3
4) 加权平均法
根据重要性及其它指标,将三个分量以不同的权值进行加权平均。由于人眼对绿色的敏感最高,对蓝色敏感最低,因此,按下式对RGB三分量进行加权平均能得到较合理的灰度图像。
f(i,j)=0.30R(i,j)+0.59G(i,j)+0.11B(i,j))
在我们的程序中,我们采用的是加权平均法进行灰度化。
下面为实现的代码:
//灰度化
void CBMPSampleDlg::ConvertToGray()
{
m_Dib.AttachMapFile("1.bmp", TRUE);
m_Dib.CopyToMapFile("灰度化.bmp");
RGBTRIPLE *rgbtri = (RGBTRIPLE *)m_Dib.m_lpImage;
int iSize = m_Dib.GetSizeImage();
BITMAPINFOHEADER * pBmiHeader = (BITMAPINFOHEADER *)m_Dib.m_lpBMIH;
int iGrayvalue = 0;
//遍历每一个像素
for(int i = 0; i < iSize/( pBmiHeader->biBitCount / 8); i++)
{
iGrayvalue = int( rgbtri[i].rgbtBlue * 0.11 + rgbtri[i].rgbtGreen * 0.59 + rgbtri[i].rgbtRed * 0.3 );
rgbtri[i].rgbtRed = (BYTE) iGrayvalue;
rgbtri[i].rgbtGreen = (BYTE) iGrayvalue;
rgbtri[i].rgbtBlue = (BYTE) iGrayvalue;
}
DrawPic();
}
在上述代码中,通过遍历每一个像素,然后计算该像素的三个分量的加权平均值,将三个分量设置成同一个值,这样就实现了对图像的灰度化处理。
4.模糊化
图4 模糊化处理效果
其实所谓的模糊化,就是将各个像素的相邻的像素的各个分量的值相加,然后除以2就可以实现对图像的模糊处理。
下面给出代码:
//模糊化
void CBMPSampleDlg::Blur()
{
m_Dib.AttachMapFile("1.bmp", TRUE);
m_Dib.CopyToMapFile("模糊化.bmp");
RGBTRIPLE *rgbtri = (RGBTRIPLE *)m_Dib.m_lpImage;
int iSize = m_Dib.GetSizeImage();
BITMAPINFOHEADER * pBmiHeader = (BITMAPINFOHEADER *)m_Dib.m_lpBMIH;
LONG lHeight = pBmiHeader->biHeight;
LONG lWidth = pBmiHeader->biWidth;
for (int y = 0 ; y < lHeight; y++) {
for (int x = 2 ; x < lWidth; x++, rgbtri ++) {
rgbtri->rgbtRed = (BYTE) ((rgbtri->rgbtRed + rgbtri[2].rgbtRed) >> 1);
rgbtri->rgbtGreen = (BYTE) ((rgbtri->rgbtGreen + rgbtri[2].rgbtGreen) >> 1);
rgbtri->rgbtBlue = (BYTE) ((rgbtri->rgbtBlue + rgbtri[2].rgbtBlue) >> 1);
}
rgbtri +=2;
}
DrawPic();
}
上面的代码同样是遍历每一个像素将前一个像素和后一个像素相加,然后将获得的值右移一位,这样就能实现除以2的效果,之所以做位运算,是因为位运算的速度比除法运算要快很多。