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

来源:互联网 发布:mac 必应词典下载 编辑:程序博客网 时间:2024/06/03 15:00

格式原理参考

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位为主RGBtypedef struct{int biSize;//说明信息头结构所需要的字节数int biWidth;//图象的宽度,以象素为单位int biHeight;//图象的高度,以象素为单位 int biPlanes;//bmp图片的平面属,显然显示器只有一个平面,所以恒等于1short 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以上的BMPpublic: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,32p = 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;//位图大小,BBitMapInfoHeader 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;elseit1[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;//*2else if (BMIHeader.biBitCount == 24)size *=3;else if (BMIHeader.biBitCount == 32)size <<=2;//*4bitMap = new BYTE[size];//一个像素一位 //扫描行实际长度(包含填充)ulong rows_len = ((BMIHeader.biWidth*BMIHeader.biBitCount + 31) >>5)<<2;//1-8位,我们这里默认使用一BYTE表示每个像素点if (BMIHeader.biBitCount == 1) {//Doneint 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) {//DoneBYTE 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) {//Donefor (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) {//Donefor (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) { //Donefor (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) {//Donefor (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+R5uint n = (row*BMIHeader.biWidth + col) * 2;val[0] = (0x7C & bitMap[n]) >> 2;//01111100val[1] &= 0x00;val[1] |= ((0x3 & bitMap[n]) << 3);//0000 0011val[1] |= ((0xE0 & bitMap[n + 1]) >> 5);//1110 0000val[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); //BGRABYTE 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];elsereturn 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;}



原创粉丝点击