数字图像处理基础【一】——BMP文件结构,读写

BMP是位图BitMaP的缩写,是一种常见的图像文件格式。其通常由四个部分组成:

  1. 位图文件头(Bitmap File Header)
  2. 位图信息头(Bitmap Info Header)
  3. 调色板(RGB Quad)
  4. 位图数据(Pixel Array)
若在windows环境下,这些结构都定义在头文件中。当然可以自己定义,下面详细分析这些结构。

  • 位图文件头(BITMAPFILEHEADER)。其长度固定为14字节,具体结构如下:
typedef struct tagBITMAPFILEHEADER {
		WORD bfType; 
		DWORD bfSize;
		WORD bfReserved1;
		WORD bfReserved2;
		DWORD bfOffBits;
	} BITMAPFILEHEADER;
bfType是文件类型,必须是0x424D,也就是字符串"BM"。
bfSize是整个BMP文件的大小,单位是字节,接下来两个是保留字节,必须为0
bfOffBits是从开始到位图数据的偏移值,以字节为单位,如256色的BMP其值为1078 (14+40+256*4)。
  • 位图信息头(Bitmap Info Header)。其长度固定为40个字节,具体结构如下:
typedef struct tagBITMAPINFOHEADER {
		DWORD biSize; // the size of this header(40 bytes)
		LONG biWidth; // the bitmap width in pixels
		LONG biHeight; // the bitmap height in pixels
		WORD biPlanes; // the number of color planes.Must be 1
		WORD biBitCount; // the number of bits per pixel
		DWORD biCompression; // the compression method being used
		DWORD biSizeImage; // the size of the raw bitmap data
		LONG biXPelsPerMeter; // the horizontal resolution. (pixel per meter, signed integer)
		LONG biYPelsPerMeter; // the vertical resolution. (pixel per meter, signed integer)
		DWORD biClrUsed; // the number of colors in the color palette
		DWORD biClrImportant; // the number of important colors used, generally ignored.
	}BITMAPINFOHEADER;
一些比较重要的字段:
biWidth:每行拥有像素个数
biHeight:每列拥有像素个数
biBitCount:每个像素所需要的字节数表示。如8(256色),则需要一个字节,24(真彩色),则需要3个字节。
biSizeImage:图像数据部分所占用的大小,以字节为单位。注意和文件头中的bfSize区别,其值等于一个扫描行字节数(后面会提到)*biHeight。
这里注意两点:
  1. windows规定一个扫描行字节数必须是4的倍数,则可以得出公式:bytePerLine=(biWidth *biBitCount / 8 + 3) / 4 * 4
  2. 将图像看成是一个矩阵阵列,那么图像左下角对应矩阵中(0,0),右上角对应矩阵中(width, height)。
  • 调色板(RGB Quad)。一个调色板占4个字节,个数一般有biBitCount决定(2的biBitCount次幂)。具体结构如下:
typedef struct tagRGBQUAD {
		BYTE rgbBlue;
		BYTE rgbGreen;
		BYTE rgbRed;
		BYTE rgbReserved;
	}RGBQUAD;
最后一个字段为保留字段,一般不用。
  • 位图数据(Pixel Array)

存储位图的信息,若有调色板,则存储颜色的索引值,否则(24位真彩色)存储每个像素的颜色值(一次为B、G、R)。

  1. 2值图像:一个字节存储8个像素颜色索引。
  2. 16色图像:一个字节存储2个像素颜色索引。
  3. 256色图像:一个字节存储1个像素颜色索引。
  4. 24位真彩色:三个字节存储一个像素的颜色值。
根据上面的知识,用C++(VS2005以上)实现对BMP文件的读写。

1、定义相关结构和重载输入输出操作符,以方便以后的操作:

	// image data
	typedef unsigned char* PixelArray;

	// stream operator overload, convinient for read and write
	ifstream& operator >> (ifstream&, BITMAPFILEHEADER&);
	ofstream& operator << (ofstream&, BITMAPFILEHEADER&);

	ifstream& operator >> (ifstream&, BITMAPINFOHEADER&);
	ofstream& operator << (ofstream&, BITMAPINFOHEADER&);

	ifstream& operator >> (ifstream&, RGBQUAD&);
	ofstream& operator << (ofstream&, RGBQUAD&);


2、读BMP操作可以如下实现:
void IOHandle::ReadBMP(BMPBasic& bmpFile) { // read file
	ifstream readHandle(bmpFile.fileName.c_str(), ios::in | ios::binary);

	if (!readHandle) {
		cerr << "error: can't open file " << bmpFile.fileName << endl;
		exit(EXIT_FAILURE);
	}

	readHandle >> bmpFile.fileHeader;
	readHandle >> bmpFile.infoHeader;

	if (bmpFile.infoHeader.biBitCount != 24) { // read color palette
		bmpFile.colorTableNum = static_cast(pow(2.0, static_cast(bmpFile.infoHeader.biBitCount)));
		bmpFile.colorPalette.resize(bmpFile.colorTableNum);
		for (unsigned long i = 0; i != bmpFile.colorTableNum; ++i) {
			readHandle >> bmpFile.colorPalette[i];
		}
	}

	bmpFile.pixelArray = new unsigned char[bmpFile.infoHeader.biSizeImage];
	readHandle.read(reinterpret_cast (bmpFile.pixelArray), (bmpFile.infoHeader.biSizeImage) * sizeof (unsigned char));

	bmpFile.pixelNum = bmpFile.infoHeader.biWidth * bmpFile.infoHeader.biHeight;
	bmpFile.SetBytePerLine((bmpFile.infoHeader.biWidth * bmpFile.infoHeader.biBitCount / 8 + 3) / 4 * 4);
}

3、相应的写BMP操作可以如下实现:
void IOHandle::GenBMP(BMPBasic& bmpFile, const string& genFileName) const { // write bmp file
	ofstream writeHandle(genFileName.c_str(), ios::out | ios::binary);

	if (!writeHandle) {
		cerr << "error: can't open file " << genFileName << endl;
		exit(EXIT_FAILURE);
	}

	writeHandle << bmpFile.fileHeader;
	writeHandle << bmpFile.infoHeader;

	for (unsigned short i = 0; i != bmpFile.colorTableNum; ++i) {
		writeHandle << bmpFile.colorPalette[i];
	}

	writeHandle.write(reinterpret_cast (bmpFile.pixelArray), bmpFile.fileHeader.bfSize * sizeof (unsigned char));
}

4、在控制台输出一些信息验证:
void BMPBasic::ShowInformation() { // output some infomation
	cout << "type(bfType): " << hex << fileHeader.bfType << dec << "\nfile size(bfSize): " << fileHeader.bfSize << endl;
	cout << "bitcount(biBitCount): " << infoHeader.biBitCount << " " << "\nwidth: " << infoHeader.biWidth << 
		"\nheight: " << infoHeader.biHeight << "\nimage data size(biSizeImage): " << infoHeader.biSizeImage << endl;
	cout << "size per line: " << bytePerLine << endl;

	cout << "bfOffBits: " << fileHeader.bfOffBits << endl;
}

【参考资料】
1、http://blog.sina.com.cn/s/blog_7097d41b0100uf3x.html
2、http://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx
3、http://zh.wikipedia.org/wiki/BMP

你可能感兴趣的:(C++,图像处理)