BMP图片格式解析并显示示例程序

格式原理参考

http://blog.csdn.net/o_sun_o/article/details/8351037
http://blog.csdn.net/lanbing510/article/details/8176231
https://en.wikipedia.org/wiki/BMP_file_format

关于解析以上几个网站,尤其wiki写的很好,我就不赘述了。


几点说明

1.BMP格式众多,在这里我只实现了无压缩格式的BMP解析,因为有压缩的很少用,并且解析只是多几个位运算而已。

2.在这里的解析适用于windows3以后的BMP(估计现在一般没人用windows3之前的了吧),因为这里的调色板被我固定了大小,并且因为不解析压缩的,所以也没有做成调色板与识别压缩像素的RGB掩模共存。

3.关于解析后的存储,biBitCount = 1,4,8。解析后在bitMap中每一个像素占1B,biBitCount=16/24/32,分别占2/3/4B 

4.调色板中每一元素为BGRA(4B),24位图在bitMap中每一元素为BGR值,32位图在bitMap中每一元素为BGRA值。

5.在bitMap中像素存放是从图像左下角开始的,但在getPixelAt(row,col)中我自动作了换算,所以这里的row、col是针对左上角的。

6.测试时用的图片格式有二值bmp,4B bmp,8B bmp,24B bmp,32B bmp。

7.16位图的像素BGR大小是5B+5B+5B,最高位去除。

8.原来这里的bmp像素区对其超级简单,就是一行实际有多少B,我们读时一次读一行,取每行前面的有效数据即可。

9.解析后的数据显示我用了Opencv( 所以我写的解析部分相当于一个残缺版的imread() :-D )。

PS:优化少以及以上问题主要是因为懒,趁着我的研友出去比赛,不想学习,来完成这个之前的烂尾作,做完舒了一口气,笑。


代码

Image_BMP_Class.h

#ifndef _IMAGE_BMP_H_
#define _IMAGE_BMP_H_
#include<iostream>
#include <string>
using namespace std;


typedef unsigned char BYTE;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;

// 24位为主RGB
typedef struct
{
	int biSize;//说明信息头结构所需要的字节数
	int biWidth;//图象的宽度,以象素为单位
	int biHeight;//图象的高度,以象素为单位 
	int biPlanes;//bmp图片的平面属,显然显示器只有一个平面,所以恒等于1
	short biBitCount;//说明比特数/象素,其值为1、4、8、16、24、或32。
	int biCompression;//说明图象数据压缩的类型
	int biSizeImage;//说明图象的大小,以字节为单位
	int biXPelsPerMeter;//水平分辨率
	int biYPelsPerMeter;//水平分辨率
	int biClrUsed;//位图实际使用的彩色表中的颜色索引数
	int biClrImportant;//对图象显示有重要影响的颜色索引的数目
} BitMapInfoHeader; 


class Palette{
	BYTE * ptr;
	uint len;
	//默认四字节 ,所以只适于Windows3.0以上的BMP
public:
	
	Palette() {
		ptr = NULL;
		len = 0;
	}
	~Palette() {
		if(this->ptr!=NULL)
			delete [] ptr;
		ptr = NULL;
	}
	void alloc_mem(uint n) {
		len = n;
		if(len>0)
			ptr = new BYTE[n*4];
	}
	BYTE* getPtr() {
		return ptr;
	}
	BYTE* getPtrAt(uint n) {
		return ptr + n*4;
	}
	void read(BYTE* start_ptr) {
		for(uint i=0;i< len*4;i++){
			ptr[i] = start_ptr[i];
		}
	}
	void show() {
		for(int i=0;i<len;i++) {
			std::cout<<i<<'\t';
			for(int j=0;j<4;j++)
				std::cout<<int(*(ptr+i*4+j))<<' ';
			std::cout<<std::endl;
		}	
	}
};

class BMPData {
public:
	ushort biBitCount;
	BYTE *ptr;
	uint rows, cols;
	ulong size;
	//其中坐标以图片左下角开始 
public:
	BMPData() {
		biBitCount = 0;
		ptr = NULL;
		rows = cols = 0;
		size = 0;
	};
	~BMPData() {
		if (ptr) delete[] ptr;
	}
	void set_type(ushort biBitCount, uint rows, uint cols) {
		this->biBitCount = biBitCount;
		uint p = 1;
		//计算该数据在内存理论多少字节 
		if (biBitCount>8) {
			//16,24,32
			p = biBitCount / 8;
		}
		size = rows*cols*p;
		ptr = new BYTE[size];

	}

	void read(BYTE *start_ptr) {
		
	}
	BYTE* Ptr() {
		return ptr;
	}
	BYTE* at(int i, int j) {
		return ptr + i*rows + j;
	}
};


class Image_bmp
{
	
private:
	string name;
	string date;
	short bfReserved1, bfReserved2;
	unsigned int bfOffBits;//文件头开始到实际的图象数据之间的字节
	Palette * palette;
	uint size_palette;
	BYTE * bitMap;
	
	
public:	
	string bfType;
	int bfSize;		//位图大小,B
	BitMapInfoHeader BMIHeader;
	
public:
	Image_bmp();
	Image_bmp(const char* name);
	bool open(const char* name);
	//void save(const char* name);
	void getPixelAt(int row, int col, BYTE val[]);
	void showHead();
	void showPalette();
	void show();
	~Image_bmp();
private:
	bool readHead(BYTE *start_ptr, uint offset);
	bool readData(BYTE *start_ptr,uint offset);
};


#endif 
//_IMAGE_BMP_H_


Image_BMP.cpp

#include<iostream>
#include<fstream>
#include<cmath>
#include <string>
#include"Image_BMP_Class.h"
#include<opencv2\opencv.hpp>
#include <assert.h>
using namespace std;

int ToInt(BYTE x,BYTE &it1,BYTE &it2)
{
	//一字节转换为int 
	int b;
	it1 = it2 = 0;
	it1 =(0xF & x);
	it2 = (0xF0 & x) >> 4;
	return int(x);
}
void ToInt(BYTE x,int it1[])
{
	int b;
	//for(int i =0;i<8;++i) it1[i] = 0;
	for(int i=0;i<8;++i)
	{
		b= x&1;
		if(b!=0)
			it1[i] = 1;
		else
			it1[i] = 0;
		x=x>>1;
	}
}

int ToInt2(BYTE item[])
{
	//两字节转换为int 
	BYTE x1,x2,x3,x4;
	ToInt(item[0],x1,x2);
	ToInt(item[1],x3,x4);
	return x1+x2*16+x3*16*16+x4*pow(16,3);
}

int ToInt4(BYTE item[])
{
	//四字节组成一个int 
	BYTE x1,x2,x3,x4,x5,x6,x7,x8;
	ToInt(item[0],x1,x2);
	ToInt(item[1],x3,x4);
	ToInt(item[2],x5,x6);
	ToInt(item[3],x7,x8);
	return  x1+x2*16+x3*16*16+x4*pow(16,3)+
			x5*pow(16,4)+x6*pow(16,5)+
			x7*pow(16,6)+x8*pow(16,7);
}

Image_bmp::Image_bmp()
{
	name = date =bfType ="";
	bfReserved1=bfReserved2=bfSize=0;
	bfOffBits=0;
	bitMap=NULL;
	palette=NULL;
	BMIHeader.biSize=BMIHeader.biWidth=
	BMIHeader.biHeight=BMIHeader.biPlanes=
	BMIHeader.biBitCount=BMIHeader.biCompression=
	BMIHeader.biSizeImage=BMIHeader.biXPelsPerMeter=
	BMIHeader.biYPelsPerMeter=BMIHeader.biClrUsed=
	BMIHeader.biClrImportant=0;
} 

Image_bmp::Image_bmp(const char *filename)
{
	name = date =bfType ="";
	bfReserved1=bfReserved2=bfSize=0;
	bfOffBits=0;
	bitMap=NULL;
	palette=NULL; 
	BMIHeader.biSize=BMIHeader.biWidth=
	BMIHeader.biHeight=BMIHeader.biPlanes=
	BMIHeader.biBitCount=BMIHeader.biCompression=
	BMIHeader.biSizeImage=BMIHeader.biXPelsPerMeter=
	BMIHeader.biYPelsPerMeter=BMIHeader.biClrUsed=
	BMIHeader.biClrImportant=0;
	
	
	if (!open(filename)) {
		cout << "read fail!" << endl;
	}
	
}

Image_bmp::~Image_bmp()
{
	if(palette)
		delete palette;
	if(bitMap)
		delete[] bitMap;
}

bool Image_bmp::readHead(BYTE *start_ptr,uint offset =6)
{
	
	
	//已过头部6B 
	
	//保留段
	//in>>item[0]>>item[1]>>item[2]>>item[3];
	bfReserved1= ToInt2(start_ptr+offset);
	offset+=2;
	bfReserved2= ToInt2(start_ptr+offset);
	offset+=2;
	
	//有效数据偏移量 
	bfOffBits = *((int*)(start_ptr+offset)); 
	offset+=4;
	/*位图信息头*/ 
	//位图信息头大小  
	BMIHeader.biSize= *((int*)(start_ptr+offset)); 
	offset+=4;
	//图宽 
	BMIHeader.biWidth = *((int*)(start_ptr+offset)); 
	offset+=4;
	//图高 
	BMIHeader.biHeight = *((int*)(start_ptr+offset)); 
	offset+=4;
	//const 颜色平面数 == 1 
	BMIHeader.biPlanes = ToInt2(start_ptr+offset);
	offset+=2;
	//比特率/像素
	BMIHeader.biBitCount = ToInt2(start_ptr+offset);
	offset+=2;
	//压缩类型 
	BMIHeader.biCompression =  *((int*)(start_ptr+offset)); 
	offset+=4;
	//图像大小
	BMIHeader.biSizeImage =  *((int*)(start_ptr+offset)); 
	offset+=4;
	//水平分辨率
	BMIHeader.biXPelsPerMeter =  *((int*)(start_ptr+offset)); 
	offset+=4;
	//垂直分辨率
	BMIHeader.biYPelsPerMeter =  *((int*)(start_ptr+offset)); 
	offset+=4;
	//用到的颜色索引数,0表示全用 
	BMIHeader.biClrUsed = *((int*)(start_ptr+offset)); 
	offset+=4;
	//重要颜色的索引的数目 
	BMIHeader.biClrImportant =  *((int*)(start_ptr+offset)); 
	offset+=4;
	
	palette = new Palette();
	if(BMIHeader.biBitCount == 1) 
		size_palette = 2;
	else if(BMIHeader.biBitCount == 4) 
		size_palette = 16;
	else if(BMIHeader.biBitCount == 8) 
		size_palette = 256;
	else if((BMIHeader.biBitCount==16|| BMIHeader.biBitCount==32)
			&&BMIHeader.biCompression==3)
		size_palette = 1;
	else if(BMIHeader.biCompression==0)
		size_palette = 1;
		
	palette->alloc_mem(size_palette);
	palette->read(start_ptr+offset);
	offset+=size_palette*4;
	//cout<<"offset"<<offset<<endl;
	//cout<<bfOffBits<<endl;
	//palette->show();
	return true;
}

bool Image_bmp::readData(BYTE *start_ptr, uint offset ) {

	start_ptr += offset;
	//分配内存
	uint size = BMIHeader.biHeight*BMIHeader.biWidth;
	if (BMIHeader.biBitCount == 16)
		size <<=1;//*2
	else if (BMIHeader.biBitCount == 24)
		size *=3;
	else if (BMIHeader.biBitCount == 32)
		size <<=2;//*4
	bitMap = new BYTE[size];


	//一个像素一位 
	//扫描行实际长度(包含填充)
	ulong rows_len = ((BMIHeader.biWidth*BMIHeader.biBitCount + 31) >>5)<<2;
	//1-8位,我们这里默认使用一BYTE表示每个像素点
	if (BMIHeader.biBitCount == 1) {	//Done
		int val[8];
		for (uint i = 0; i<BMIHeader.biHeight; i++) {
			BYTE *row_ptr = start_ptr + i*(rows_len);
			uint cnt = 0;
			for (uint j = 0; j<BMIHeader.biWidth; j+=8,cnt++) { 
				ToInt(*(row_ptr + cnt), val);
				for (int k = 0; k < 8 && j + k < BMIHeader.biWidth; k++)
					bitMap[i*BMIHeader.biWidth + j + k] = val[7-k];
			}
		}
	}
	else if (BMIHeader.biBitCount == 4) {	//Done
		BYTE val[2];
		for (uint i = 0; i<BMIHeader.biHeight; i++) {
			BYTE *row_ptr = start_ptr + i*(rows_len);
			uint cnt = 0;
			for (uint j = 0; j<BMIHeader.biWidth; j += 2, cnt++) {
				ToInt(*(row_ptr + cnt), val[0],val[1]);
				bitMap[i*BMIHeader.biWidth + j] = val[1];
				if(j+1<BMIHeader.biWidth)
					bitMap[i*BMIHeader.biWidth + j + 1] = val[0];
			}
		}
	}
	else if (BMIHeader.biBitCount == 8) {	//Done
		for (uint i = 0; i<BMIHeader.biHeight; i++) {
			BYTE *row_ptr = start_ptr + i*(rows_len);
			for (uint j = 0; j<BMIHeader.biWidth; j++) {
				bitMap[i*BMIHeader.biWidth + j] = row_ptr[j];
			}
		}
	}
	else if (BMIHeader.biBitCount == 16) {	//Done
		for (uint i = 0; i<BMIHeader.biHeight; i++) {
			BYTE *row_ptr = start_ptr + i*(rows_len);
			for (uint j = 0; j<BMIHeader.biWidth*2; j++) {
				bitMap[i*BMIHeader.biWidth*2 + j] = row_ptr[j];
			}
		}
	}
	else if (BMIHeader.biBitCount == 24) { //Done
		for (uint i = 0; i<BMIHeader.biHeight; i++) {
			BYTE *row_ptr = start_ptr + i*(rows_len);
			for (uint j = 0; j<BMIHeader.biWidth * 3; j++) {
				bitMap[i*BMIHeader.biWidth*3 + j] = row_ptr[j];
			}
		}
	}
	else if (BMIHeader.biBitCount == 32) {	//Done
		for (uint i = 0; i<BMIHeader.biHeight; i++) {
			BYTE *row_ptr = start_ptr + i*(rows_len);
			for (uint j = 0; j<BMIHeader.biWidth * 4; j++) {
				bitMap[i*BMIHeader.biWidth*4 + j] = row_ptr[j];
			}
		}
	}
	return true;
}

void Image_bmp::showHead()
{
	
	cout << "name " << name << endl;
	cout << "bfOffBits " << bfOffBits << endl;
	cout << "BMIHeader.biSize " << BMIHeader.biSize << endl;
	cout << "BMIHeader.biWidth " << BMIHeader.biWidth << endl;
	cout << "BMIHeader.biHeight " << BMIHeader.biHeight << endl;
	cout << "BMIHeader.biPlanes " << BMIHeader.biPlanes << endl;
	cout << "BMIHeader.biBitCount " << BMIHeader.biBitCount << endl;
	cout << "BMIHeader.biCompression " << BMIHeader.biCompression << endl;
	cout << "BMIHeader.biSizeImage " << BMIHeader.biSizeImage << endl;
	cout << "BMIHeader.biXPelsPerMeter " << BMIHeader.biXPelsPerMeter << endl;
	cout << "BMIHeader.biYPelsPerMeter " << BMIHeader.biYPelsPerMeter << endl;
	cout << "BMIHeader.biClrUsed " << BMIHeader.biClrUsed << endl;
	cout << "BMIHeader.biClrImportant " << BMIHeader.biClrImportant << endl;
	cout << "Disp of Bmp's header" << endl;
} 
void Image_bmp::showPalette() {
	if(palette)
		palette->show();
}

void Image_bmp::getPixelAt(int row, int col, BYTE val[]) {
	//返回(row,col)处的存放于val中的B-G-R-A值
	
	row = BMIHeader.biHeight - row-1;				//存储的信息从图片左下角开始,这里转换相对于左上角
	if (BMIHeader.biBitCount == 32) {
		if (BMIHeader.biCompression == 0) {			//32位无压缩
			uint n = (row*BMIHeader.biWidth + col) * 4;
			for(int k=0;k<4;k++)
				val[k] = bitMap[n+k];
		}
		else {										//32位压缩(带掩模)

		}
	}
	else if (BMIHeader.biBitCount == 24) {		
		uint n = (row*BMIHeader.biWidth + col) * 3;
		for (int k = 0; k<3; k++)
			val[k] = bitMap[n + k];
	}
	else if (BMIHeader.biBitCount == 16) {
		if (BMIHeader.biCompression == 0) {			//16位无压缩B5+G5+R5
			uint n = (row*BMIHeader.biWidth + col) * 2;
			val[0] = (0x7C & bitMap[n]) >> 2;		//01111100
			val[1] &= 0x00;
			val[1] |= ((0x3 & bitMap[n]) << 3);		//0000 0011
			val[1] |= ((0xE0 & bitMap[n + 1]) >> 5);//1110 0000
			val[2] |= (0x1F & bitMap[n + 1]);		//0001 1111
		}
		else {										//16位压缩(带掩模)

		}
	}
	else if (BMIHeader.biBitCount <= 8) {			// 1 4 8 位,必用调色板
		uint n = (row*BMIHeader.biWidth + col);
		BYTE * ptr = this->palette->getPtrAt(int(bitMap[n]));
		for (int i = 0; i < 4; i++)
			val[i] = ptr[i];
	}
}

void Image_bmp::show() {
	if (!bitMap) return;
	using namespace cv;
	Mat mat(BMIHeader.biHeight, BMIHeader.biWidth, CV_8UC3); //BGRA
	BYTE val[4];
	//cout << "mat.rows" << mat.rows << endl;
	{
		for (int i = 0; i < mat.rows; ++i) {
			for (int j = 0; j < mat.cols; j++) {
				cv::Vec3b &rgba = mat.at<cv::Vec3b>(i, j);
				getPixelAt(i, j, val);
				for(int k=0;k<3;k++)
					rgba[k] = val[k];
			}
		}
	}

	
	imshow("2333333333 494D4C",mat);
	//waitKey(0);
}
bool Image_bmp::open(const char* file_name) {
	char item[4];
	ifstream fcin(file_name, ios::in | ios::binary);
	if (fcin.fail()) {
		return false;
	}
	fcin.seekg(0, ios::beg);
	//读入文件格式 
	fcin >> item[0] >> item[1];
	if (item[0] != 'B' || item[1] != 'M') {
		fcin.close();
		return false;
	}
	this->name = string(file_name);
	bfType = "BM";
	fcin.read(item, 4);
	//读入位图大小
	bfSize = ToInt4((BYTE*)item);
	//cout << "bfSize " << bfSize << endl;

	//加载所有数据
	BYTE *start_ptr = new BYTE[bfSize + 1];
	if (!start_ptr) {
		cout << "bad malloc" << endl;
		return false;
	}
	fcin.seekg(0, ios::beg);
	fcin.read((char*)start_ptr, bfSize);
	fcin.close();
	
	readHead(start_ptr, 6);
	readData(start_ptr, bfOffBits);
	if(start_ptr)
		delete[] start_ptr;
	return true;
}


main.cpp

#include <iostream>
#include <opencv2\opencv.hpp>
#include"Image_BMP_Class.h"

int main(int argc, char** argv) {
	
	char *file_name = NULL;
	if (argc == 2)
		file_name = argv[1];
	else
		return 0;
	Image_bmp pic(file_name);
	pic.showHead();
	pic.show();
	cout << "Creat by YWF CSDN_Blog:http://blog.csdn.net/hffhjh111?viewmode=contents" << endl;
	//Image_bmp pic("8.bmp");
	//pic.showHead();
	//p.showPalette();
	//pic.show();
	cv::waitKey(0);
	return 0;
}



猜你喜欢

转载自blog.csdn.net/hffhjh111/article/details/72781634
今日推荐