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

论坛 期权论坛 脚本     
匿名网站用户   2020-12-19 21:55   34   0

格式原理参考

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;
}



分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP