格式原理参考
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
#include
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;ibiBitCount = 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
#include
#include
#include
#include"Image_BMP_Class.h"
#include
#include
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"<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; ishow();
}
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(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
#include
#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;
}