本文档最早发布于级灰度BMP文件读写的源代码!首先要明白256级灰度BMP文件的格式1.首先是一个14个字节的文件头,定义如下typedefstructtagBITMAPFILEHEADER{WORDbfType;DWORDbfSize;WORDbfReserved1;WORDbfReserved2;DWORDbfOffBits;}BITMAPFILEHEADER,*PBITMAPFILEHEADER;bfType是表明BMP文件类型的数据,在这里我们填入的是0x4d42,其实就是BM两个字,bfSize是文件大小,bfOffBits是文件头到数据块的偏移量,对于256级灰度图,就是1078个字节,后面会做描述2.接下来是40个字节的是描述位图属性的40个字节typedefstructtagBITMAPINFOHEADER{DWORDbiSize;LONGbiWidth;LONGbiHeight;WORDbiPlanes;WORDbiBitCount;DWORDbiCompression;DWORDbiSizeImage;LONGbiXPelsPerMeter;LONGbiYPelsPerMeter;DWORDbiClrUsed;DWORDbiClrImportant;}BITMAPINFOHEADER,*PBITMAPINFOHEADER;这里面只有biWidth表示宽度,biPlanes表示高度,biBitCount对于256级灰度正好是83.由于是256级灰度图,那么有256个调色板数据,每个调色板是如下定义的typedefstructtagRGBQUAD{BYTErgbBlue;BYTErgbGreen;BYTErgbRed;BYTErgbReserved;}RGBQUAD,*PRGBQUAD;调色板数据其实告诉了显示器实际显示的时候的具体颜色,所以调色板长度是1024字节4.最后是按行组织的图像数据,但这些数据并不是简单的按照图像的高度宽度w*h的数组数据这些数据最重要的特点是a.按行组织,每行宽度是w,但是要进行4个字节的对齐。比如如果是图像宽度是253,那么数据对齐后一行还是有256个字节。对齐可以用下面的宏来计算#defineGET_ALIGN(x)(((x+3)/4)*4)b.图像数据是倒行的,也就是数据第一行对应图像最后一行,最后一行数据对应第一行图像的实际数据之前的偏移量是:sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD)=14+40+1024=1078个字节。下面是实际的BMP文件输入输出函数代码:/*********************************************/从文件读入内存代码:pw返回宽度;ph返回高度;函数返回内存指针;/*********************************************/unsignedchar*read_bmp(constchar*pszFileName,int*pw,int*ph){BITMAPFILEHEADERbfh;BITMAPINFOHEADERbmh;FILE*fp;unsignedchar*pImg=NULL;inti;fp=fopen(pszFileName,rb);//二进制打开if(fp==NULL)returnNULL;fread(&bfh,sizeof(BITMAPFILEHEADER),1,fp);fread(&bmh,sizeof(BITMAPINFOHEADER),1,fp);//判断是否8bit的图像if(bfh.bfType!=0x4d42&&bmh.biBitCount!=8)returnNULL;pImg=(unsignedchar*)malloc(bmh.biWidth*bmh.biHeight);//开辟空间*pw=bmh.biWidth;*ph=bmh.biHeight;for(i=0;ibmh.biHeight;i++){fseek(fp,1078+(bmh.biHeight-i-1)*GET_ALIGN(bmh.biWidth),SEEK_SET);fread(pImg+i*bmh.biWidth,1,bmh.biWidth,fp);}fclose(fp);returnpImg;}/*********************************************/bmp文件输出:将数据从*pImg存入*pszFileName指向的文件中w指定宽度,h指定高度。/*********************************************/voidwrite_bmp(constchar*pszFileName,unsignedchar*pImg,intw,inth){BITMAPFILEHEADERbfh;BITMAPINFOHEADERbmh;RGBQUADbmiColors[256];FILE*fp;inti;fp=fopen(pszFileName,wb);if(fp==NULL)return;//写位图文件头bfh.bfType=0x4d42;bfh.bfSize=GET_ALIGN(w)*h+1078;bfh.bfOffBits=1078;bfh.bfReserved1=0;bfh.bfReserved2=0;//设置位图参数bmh.biSize=40;bmh.biWidth=w;bmh.biHeight=h;bmh.biPlanes=1;bmh.biBitCount=8;bmh.biCompression=0;bmh.biSizeImage=GET_ALIGN(w)*h;bmh.biXPelsPerMeter=0;bmh.biYPelsPerMeter=0;bmh.biClrUsed=0;bmh.biClrImportant=0;//创建256个灰度调色板for(i=0;i256;i++){bmiColors[i].rgbRed=(BYTE)i;bmiColors[i].rgbGreen=(BYTE)i;bmiColors[i].rgbBlue=(BYTE)i;}fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fp);//bfh指向被写入的数据,fp指向被改写的文件fwrite(&bmh,sizeof(BITMAPINFOHEADER),1,fp);fwrite(bmiColors,sizeof(RGBQUAD),256,fp);//调色板写入文件//行倒过来,对齐后写入for(i=0;ih;i++){fwrite(pImg+(h-i-1)*w,1,GET_ALIGN(w),fp);}fclose(fp);}这是一个用于测试的指纹图2.对图像进行反色,理解调色板介绍了BMP格式,里面有一个这么一段,是设置灰度线性调色板for(i=0;i256;i++){bmiColors[i].rgbRed=(BYTE)i;bmiColors[i].rgbGreen=(BYTE)i;bmiColors[i].rgbBlue=(BYTE)i;}这段代码的作用是设置BMP文件中的调色板,意思是图像像素值为i的,实际显示的时候正好是RGB(i,i,i),这正好是个线性的灰阶。RGB(0,0,0)是纯黑色,RGB(255,255,255)是白色。大家可以打开Window附件/画图工具来看一下,选择菜单颜色/编辑颜色,在对话框中选择规定自定义颜色,就可以输入RGB的数值来看实际的颜色效果了。所以,如果我们需要反色的话,其实我们只需要简单的把这个调色板反过来就可以了,也就是for(i=0;i256;i++){bmiColors[i].rgbRed=(BYTE)(255-i);bmiColors[i].rgbGreen=(BYTE)(255-i);bmiColors[i].rgbBlue=(BYTE)(255-i);}当然我们也可以不改变调色板,把后面真正的像素值取反就可以了,也就是文件头1078字节后for(i=0;iw*h;i++)pImg[i]=~pImg[i];很简单的吧,这两种反色最后看到的效果相同,但含义是不一样的。3.做一个自己的截图软件有了前面两节的基础,我们就可以做一个简单的截图软件了。下面这个函数cut可以按照把(x0,y0),(x1,y1)两个点组成的对角线矩形区域的图像截取出来输入参数pImg,输出参数pImg2,w,h是pImg的图像的高度和宽度输入的时候要求x0x1w,y0y1hintcut(unsignedchar*pImg,unsignedchar*pImg2,intx0,inty0,intx1,inty1,intw,inth){inti,j,cnt=0;if(x00||x0w-1||y00||y0h-1)return-1;for(i=y0;iy1;i++)for(j=x0;jx1;j++)pImg2[cnt++]=pImg[i*w+j];}这样输出的图像就是宽度为(x1-x0),高度为(y1-y0)的图像了。可以用下面的代码测试一下intstep3(void){intw,h;unsignedchar*pImg,*pImg2;printf(step3:testcutimage!\n);pImg=read_bmp(test.bmp,&w,&h);if(pImg==NULL)return-1;pImg2=(unsignedchar*)malloc(100*100);cut(pImg,pImg2,10,10,110,110,w,h);write_bmp(cut.bmp,pImg2,100,100);free(pImg);free(pImg2);return1;}这样就把一个位置于原图像(10,10)-(110,110),大小为100x100的图像截取出来了。并且保存到了cut.bmp中4.保留一点颜色,理解调色板其实在第二节我们已经介绍了调色板,在那里我们将图像进行了反色,我们采用的其中一种方法是把调色板进行反色。在这里我们介绍一下如何在调色板里保留一些颜色,这样我们可以在图像上画图,有利于显示。先介绍我们这一期的画图函数/**************************************************/描绘细节点函数(细节点是指纹中的一个概念,在后续文章中会介绍),这个函数可以指定一个坐标位置x,y,把图像上这个点的附近画一个小圆圈。像素值是1。/**************************************************/voidpaint_min(unsignedchar*pImg,intx,inty,intw,inth){if(x2||y2||xw-3||yh-3)return;pImg[(y-1)*w+x-2]=1;pImg[y*w+x-2]=1;pImg[(y+1)*w+x-2]=1;pImg[(y+2)*w+x-1]=1;pImg[(y+2)*w+x]=1;pImg[(y+2)*w+x+1]=1;pImg[(y+1)*w+x+2]=1;pImg[y*w+x+2]=1;pImg[(y-1)*w+x+2]=1;pImg[(y-2)*w+x+1]=1;pImg[(y-2)*w+x]=1;pImg[(y-2)*w+x-1]=1;}/**************************************************/画矩形函数,这个函数描绘了一个像素值为color的函数矩形宽度为x0到x1,高度为y0到y1;*pImg指向位图数据区。/***********************************************