打开BMP格式图像文件
BMP文件分为BITMAPFILEHEADER、BITMAPINFORHEADER、RGBQUAD三部分文件头BF包含文件的类型,文件的大小,位图数据距文件头的偏移量等,BI是说明位图的信息,有位图的颜色位数biBitCount,位图的高度宽度,以及位图数据的大小,通过读取BMP格式文件的这些信息,就能对其进行解码,打开BMP文件。
例程:
//选取文件
LPCTSTR lpszFilter = "BMP Files(*.bmp)|*.bmp|任何文件|*.*||";
CFileDialog dlg1(TRUE,lpszFilter,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,lpszFilter,NULL);
CString filename;
CFile file;
if(dlg1.DoModal()==IDOK)
{
//读入文件
filename = dlg1.GetPathName();
if(file.Open(filename,CFile::modeRead|CFile::shareDenyNone,NULL)==0)
{
AfxMessageBox("无法打开文件",MB_OK,0);
return;
}
//读取文件头,将大小为sizeof(bf)的数据传入缓冲区bf
file.Read(&bf,sizeof(bf));
//判断是否是BMP文件
if(bf.bfType!=0x4d42)
{
AfxMessageBox("非BMP文件!",MB_OK,0);
return;
}
//判断文件是否损坏
if(file.GetLength()!=bf.bfSize)
{
AfxMessageBox("文件已损坏,请检查!");
return;
}
//读取信息头
file.Read(&bi,sizeof(bi));
//计算调色板数目
numQuad = 0;
if(bi.biBitCount < 24)
{
//如果为1,4,8,则1向左移动对应的位数,对应的调色板数目为2,16,256
numQuad = 1<<bi.biBitCount;
}
//为图像信息pbi申请空间
pbi = (BITMAPINFO*)HeapAlloc(GetProcessHeap(),0,sizeof(BITMAPINFOHEADER)+numQuad*sizeof(RGBQUAD));
memcpy(pbi,&bi,sizeof(bi));
quad = (RGBQUAD*)((BYTE*)pbi+sizeof(BITMAPINFOHEADER));
//读取调色板
if(numQuad!=0)
{
file.Read(quad,sizeof(RGBQUAD)*numQuad);
}
//为图像数据申请空间
bi.biSizeImage = bf.bfSize - bf.bfOffBits;
lpBuf = (BYTE*)HeapAlloc(GetProcessHeap(),0,bi.biSizeImage);
hTempBuf=LocalAlloc(LHND,bi.biSizeImage);
lpTempBuf=(BYTE*)LocalLock(hTempBuf);
// LONG lLineBytes;
// lLineBytes =WIDTHBYTES(3*lWidth*8); //计算每行的字节数
// bi.biHeight*lLineBytes
//读取图像数据
file.Read(lpBuf,bi.biSizeImage);
//图像读取完毕,关闭文件,设置标志
memcpy(lpTempBuf,lpBuf,bi.biSizeImage);
file.Close();
flag = 1;
}
灰度处理(黑白效果):
图像灰度化就是使色彩的三种颜色分量R、G、B的值相同,由于颜色值的取值范围是[0,255],所以灰度的级别只有256种,即灰度图象仅能表现256种灰度颜色,常用有3种处理方法:
*最大值法(Maximum):R=G=B=Max(R,G,B),这种方法处理后灰度图象的亮度会偏高。
*平均值法(Average):R=G=B=(R+G+B)/3,这种方法处理后灰度图象的亮度较柔和。
*加权平均值法(Weighted Average):
R=G=B=wr*R+wg*G+wb*B,wr、wg、wb分别为R、G、B的权值。当其权值取不同的值时,能够形成不同灰度的灰度图象,由于人眼对绿色的敏感度最高,红色次之,蓝色最低,因此当wg > wr > wb时,所产生的灰度图像更符合人眼的视觉感受。通常wr=30%,wg=59%,wb=11%,图像的灰度最合理。
例程:
CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf); //图像数据起始位置的指针
LONG lWidth = pDoc->bi.biWidth; //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight; //源图像宽度,像素数
unsigned char* lpSrc; //某个像素对应的指针
int gray; //灰色对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(3*lWidth*8); //计算每行的字节数
for(i=0;i<lHeight;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+3*j;
gray = ((*lpSrc)*11+(*(lpSrc+1))*59+(*(lpSrc+2))*30)/100;
*lpSrc = gray;
*(lpSrc+1)= gray;
*(lpSrc+2) = gray;
}
}
bGray = 1;
CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf); //图像数据起始位置的指针
LONG lWidth = pDoc->bi.biWidth; //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight; //源图像宽度,像素数
unsigned char* lpSrc; //某个像素对应的指针
int gray; //灰色对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(3*lWidth*8); //计算每行的字节数
for(i=0;i<lHeight;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+3*j;
gray = ((*lpSrc)*11+(*(lpSrc+1))*59+(*(lpSrc+2))*30)/100;
*lpSrc = gray;
*(lpSrc+1)= gray;
*(lpSrc+2) = gray;
}
}
bGray = 1;
灰度拉伸:
属于图像增强技术,如果一幅图像的灰度集中在较暗的区域而导致图像偏暗,可以用灰度拉伸功能来拉伸物体灰度区间以改善图像;同样如果图像灰度集中在较亮的区域而导致图像偏亮,也可以用灰度拉伸功能来压缩物体灰度区间以改善图像质量。
我以拉伸物体灰度区间为例,将灰度在a以下的像素灰度变为0,灰度在b以上的像素变为255,然后将本来在a和b之间的像素调整到0-255。
即设灰度值为gray,则: 0 gray < a
gray = (*lpSrc-low_value)*rate+c a<gray<b
255 gray>b
其中low_value、high_value、rate、c由自己设定,若由low_value~high_value变为0~255,c取0.5用来进行四舍五入, rate = (255-0+1)/(high_value-low_value+1)
例程:该例程将灰度值靠近0的10%设为0,将靠近255的10%设为255,其他进行灰度拉伸
CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf); //图像数据起始位置的指针
LONG lWidth = (pDoc->bi.biWidth)*3; //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight; //源图像宽度,像素数
unsigned char* lpSrc; //某个像素对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(lWidth*8); //计算每行的字节数
BYTE bMap[256]; //存放灰度拉伸后的灰度值
float rate=0;
int temp;
float stretch_num[256]; //存放各个灰度级出现的次数
float stretch_p[256]; //各个灰度级出现的比率
float stretch_sum[256]; //求存放各个灰度级之前的概率和
//清空三个数组
memset(stretch_p,0,sizeof(stretch_p));
memset(stretch_sum,0,sizeof(stretch_sum));
memset(stretch_num,0,sizeof(stretch_num));
int low_value,high_value;
//求存放图象各个灰度级出现的次数
for(i=0;i<lHeight;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
stretch_num[*lpSrc]++;
}
}
//求存放图像各个灰度级的出现概率
for(i=0;i<256;i++)
{
stretch_p[i]=stretch_num[i]/(lWidth*lHeight);
}
//求存放各个灰度级之前的概率和
for(i=0;i<256;i++)
{
for(j=0;j<=i;j++)
{
stretch_sum[i]+=stretch_p[j];
}
}
//统计出两个阈值点的值
for(i=0;i<256;i++)
{
if(stretch_sum[i]<0.1) //low_value 取接近10%的总像素的灰度值
{
low_value=i;
}
if(stretch_sum[i]>0.9) //high_value取接近90%的总像素的灰度值
{
high_value=i;
break;
}
}
rate=(float)256/(high_value-low_value+1);
//进行灰度拉伸
for(i=0;i<lHeight;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
if(*lpSrc<low_value)
{
*lpSrc = 0;
}
else if(*lpSrc>high_value)
{
*lpSrc = 255;
}
else
{
temp=((*lpSrc-low_value)*rate)+0.5;
if(temp<=255)
{
*lpSrc = temp;
}
else
{
*lpSrc = 255;
}
}
}
}
图像腐蚀:
对形态学结构元素B进行z平移后,如果结构元素全部包含于集合A中,则z属于腐蚀后的集合。这个定义的意思就是从图像的第一个像素点开始依行遍历全部像素,在每个像素点上,判断是否结构元素全部位于集合A内,如果是则该点属于腐蚀后的集合,需要保留这个点,否则对改点取反(按照下面的符号约定,即将改点灰度值设置为0)。
若对黑纸上的白字进行图像腐蚀,则只针对白色的范围内,若左边或右边是黑色,则将其设置为黑色,否则不变。
例程:
CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf); //图像数据起始位置的指针
LPSTR lpTempDIB =(LPSTR)GlobalLock(pDoc->lpTempBuf);
LONG lWidth = (pDoc->bi.biWidth)*3; //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight; //源图像宽度,像素数
BYTE *lpSrc,*lpTempSrc; //某个像素对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(lWidth*8); //计算每行的字节数
memcpy(pDoc->lpTempBuf,pDoc->lpBuf,pDoc->bi.biSizeImage);
//在水平方向进行腐蚀运算
for(i=0;i<lHeight;i++)
{
for(j=3;j<lWidth-7;j=j+3)//注意为防止越界,j的范围从1到(宽度-2)
{
//lpSrc指向原图数据
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
lpTempSrc = (unsigned char*)lpTempDIB+lLineBytes*(lHeight-1-i)+j;
if (*lpTempSrc==255)
{
for(int x=0;x<7;x=x+3)
{
if((*(lpTempSrc+x-3))!=255)
{
//自身及左右邻居中若有一个不是白点,则将该点腐蚀
*lpSrc=*(lpTempSrc+x-3);
*(lpSrc+1)=*(lpTempSrc+x-3);
*(lpSrc+2)=*(lpTempSrc+x-3);
break;
}
}
}
}
}
图像相减:
在图像处理中,图像相减也是比较常用的一个概念,比如把图像和背景图像相减就可以得到物体的图像;把图像和腐蚀后的图像相减就可以得到物体的轮廓。。图像相减即对应像素的挨个相减,比较简单。
例程:
CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf); //图像数据起始位置的指针
LPSTR lpTempDIB =(LPSTR)GlobalLock(pDoc->lpTempBuf);
LONG lWidth = (pDoc->bi.biWidth)*3; //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight; //源图像宽度,像素数
unsigned char* lpSrc,*lpTempSrc; //某个像素对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(lWidth*8); //计算每行的字节数
for(i=0;i<lHeight;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
lpTempSrc = (unsigned char*)lpTempDIB+lLineBytes*(lHeight-1-i)+j;
*lpSrc = *lpSrc - *lpTempSrc;
}
}
中值滤波:
在图像处理中,经常会遇到各种噪声(即不需要的像素点),中值滤波就是舍去不需要的像素点,保留需要像素点的一种方法。它将待处理像素点上下的五个像素点保存到一个数组中(y-2,y-1,y,y+1,y+2),比较其大小,将其排序,并取中值为该点的像素值,通过该方法能去掉突出的像素点。
例程:
CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf); //图像数据起始位置的指针
LPSTR lpTempDIB = (LPSTR)GlobalLock(pDoc->lpTempBuf);
LONG lWidth = (pDoc->bi.biWidth)*3; //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight; //源图像宽度,像素数
unsigned char *lpSrc,*lpTempSrc; //某个像素对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(lWidth*8); //计算每行的字节数
int pFilter_Image_Pixel[5];//窗口像素值
int mid_pixel_value=0; // 中值
int flag;
int temp=0;// 中间变量
//中值滤波
memcpy(pDoc->lpTempBuf,pDoc->lpBuf,pDoc->bi.biSizeImage);
for(i=2;i<lHeight-2;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
lpTempSrc = (unsigned char*)lpTempDIB+lLineBytes*(lHeight-1-i)+j;
//把5*1屏蔽窗口的所有像素值放入pFilter_Image_Pixel[m]
int m=0;
for(int y=-2;y<=2;y++)
{
pFilter_Image_Pixel[m]=*(lpTempSrc-lLineBytes*y);
m++;
}
//把pFilter_Image_Pixel[m]中的值按降序排列
do{
flag=0;
for(int m=0;m<4;m++)
{
if(pFilter_Image_Pixel[m]<pFilter_Image_Pixel[m+1])
{
temp=pFilter_Image_Pixel[m];
pFilter_Image_Pixel[m]=pFilter_Image_Pixel[m+1];
pFilter_Image_Pixel[m+1]=temp;
flag=1;
}
}
}while(flag==1);
mid_pixel_value=pFilter_Image_Pixel[2];//求中值mid_pixel_value
*lpSrc=mid_pixel_value;//将中值赋给目标图像的当前点
}
}
二值化:
二值化是阈值变换的一种方法,即设定一个阈值,该点像素大于该阈值则设其为255,小于该阈值设为0,通过二值化使图像更加方便被提取特征。
例程:
if(bGray == 0)
{
AfxMessageBox("请先进行灰度处理",MB_OK);
}
else{
CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf); //图像数据起始位置的指针
LONG lWidth = (pDoc->bi.biWidth)*3;; //源图像宽度
LONG lHeight = pDoc->bi.biHeight; //源图像宽度
BYTE bThre = 125; //阈值
unsigned char* lpSrc; //某个像素对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(lWidth*8); //计算每行的字节数
for(i=0;i<lHeight;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
if((*lpSrc)<bThre)
{
*lpSrc = 0;
}else
{
*lpSrc = 255;
}
}
}
}
原图:
灰度处理后:
灰度拉伸后:
腐蚀后:
相减后:
二值化后: