用C语言读入和保存位图程序下载:http://download.csdn.net/source/1072992
用Java读入和保存位图程序下载:http://download.csdn.net/source/1090885
我这里要说的主要是如何读入8位或24位位图。因为这两种格式的位图更加具有代表性。如32位位图也只是多了一个透明度的分量罢了。其跟24位位图的格式相比变化不大。
我们常说进行数字图像处理,其实主要是处理位图的像素数据和采用何种算法去处理像素数据。可是,要想处理像素数据,首先就是要读入位图的数据。不然,处理从何谈起。
下面我将详细的说明BMP位图的文件结构,相信看完之后,对于读入位图就不是什么难事了。当然,我这里的前提是你熟悉如何读入文件。好了,废话不多说了。我们开始吧。
BMP位图的第一部分就是文件头。下面是它的结构:
typedef struct tagBITMAPFILEHEADER {
WORD
DWORD
WORD
WORD
DWORD
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;
上面的这个文件头的结构体是摘自MSDN。它的各项的含义如下:bfType代表的是文件格式,就是“BM”,用十六进制的数表示是0x4d42,用十进制的数表示就是19778。所以,从BMP文件的前两个字节我们就可以判断我们要打开的图片是不是位图,如果不是的话,我们就可以不用读了。bfSize代表的是该位图文件的大小(包含文件头、信息头、调色板(如果有)、像素数据)。它是一个DWORD类型,占4个字节。bfReserved1和bfReserved2都是保留量,因此它们应该都为0。它们合起来占4个字节。bfOffBits代表的是像素数据距离文件头的位置,也就是偏移地址。如果我们想直接读像素数据的话,可以把文件指针偏移bfOffBits个字节,然后读取就可以了,知道读到文件结尾。这样算起来,文件头一共占了2+4+2+2+4=14字节。所以,这个文件头的大小固定是14字节的。而且,还有重要的一点是,其实在保存BMP文件的时候,保存的顺序也是按照bfType、bfSize、bfReserved1、bfReserved2、bfOffBits的顺序的。因此,如果我们不想用微软的这个结构体的话,你可以按照上面的顺序依次的读入各项,用不用这个结构体,关系不是很大。不过只是说,用结构体的话,可能会带来一些方便罢了。这一点,在当你用C或Java来读取的时候,会感觉更深刻。因为在C或Java中没有微软提供的这个结构体,要么自己构造这个结构体,要么你就按找顺序和字节数依次读出各项的值。后面有我的C和Java读入位图的例子,你可以参看下。
BMP位图的第二部分就是信息头。下面是它的结构:
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG
LONG
WORD
WORD
DWORD biCompression;
DWORD biSizeImage;
LONG
LONG
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
当然了,这个结构体还是来自MSDN的。biSize代表的是该结构体的大小,即40字节。biWidth代表的是位图的宽度,以像素为单位。biHeight代表的是位图的高度,以像素为单位。biPlanes代表的位图的平面数,平面数不同,RGB各分量所排列的顺序不一样。一般常见的是1,也就是RGB各分量是按照BGR的顺序在像素数据中排列的。biBitCount代表的是位图的位数,有1、16、8、24、32等。biCompression代表是位图的压缩方法,由于BMP位图是没有压缩的,所以通常情况下它的值都为0。biSizeImage代表的是位图像素数据的大小,就是高度乘上每行像素所占的字节数。biXPelsPerMeter代表的是水平方向的分辨率,以米为单位。这个参数没有什么太大的意义,至少现在我还没发现。所以,它通常情况下都为0,不是0也没有关系。biYPelsPerMeter代表的是垂直方向的分辨率,以米为单位,通常为0。biClrUsed代表的是位图用到的颜色数,可以设置为0,代表的是全部用到了。biClrImportant代表的是位图中重要的颜色数,可以设置为0,代表的是全部都很重要。LONG和DWORD都占4个字节,WORD占2个字节,因此,该结构体(也就是信息头)共占40字节。还要说明的一点是,在保存位图的时候,我们也可以按照上面的结构体中各项的顺序来保存位图。在读入位图的时候,也可以按字节和上面的顺序来依次读入各项。用不用结构体,关系不大。还是那句话,当你用C或Java读入信息头的时候,就会理解这个顺序的重要性了。
BMP位图的调色板。它的结构如下:
typedef struct tagRGBQUAD {
BYTE
BYTE
BYTE
BYTE
} RGBQUAD
同样,上面的这个结构体仍然来自MSDN。首先要说明的一点是,只有8位位图具有调色板。而调色板是有若干项(最多256项)构成的,而每一项都是一个RGBQUAD的结构体,也就是说每一项都有一个蓝色、绿色、红色和保留量。而在8位位图中,如果我们读到的biClrUsed不为0,代表没有全部用上256中颜色的话,那代表像素数据的偏移地址就不是1078(14+40+256*4=1078)。这样其实也完全没有影响,我们在读入和保存位图的时候,对于8位位图来说,完全没有必要去读入调色板。因为我们完全可以自己去构造一个调色板,如下面的程序:
RGBQUAD *pal=new RGBQUAD[256];
for(int i=0;i<256;i++)
{
pal[i].rgbBlue=i;
pal[i].rgbGreen=i;
pal[i].rgbRed=i;
pal[i].rgbReserved=0;
}
如上的程序,我们就完成了一个调色板的构造。而且,如果你自己按照用到的颜色数再去读取调色板,也比较麻烦。再说了,也没有那个必要。
BMP文件的像素数据。我在这里只说8位和24位的位图的像素数据。我们在读取像素的值时,首先要知道的就是位图的位数。因为位数不同,像素值的排列顺序是不同的。比如8位位图,它的一个字节就代表一个像素值,也就是灰度值。而24位位图,它的一个像素占三个字节,RGB三个分量每个占一个字节。且其排列顺序是BGR,而不是RGB。也就是说,在读入24位位图的像素时,要先读B,再读G,最后是R。如果位图的高度为正数,那代表图像的左上角的那个像素存在像素矩阵的第一个值。如果位图的高度为负数,那代表图像的左下角的那个像素存在像素矩阵的第一个值。这个不同太担心,一般的位图都是将左上角的第一个像素的值存储在像素矩阵的最前面的。