前些天做一个项目,其中要对读入内存的bmp图片转格式,转换为IplImage格式提供给OpenCV处理。内存中的图片是通过Socket从远程传输过来的。仅提供图片的首地址和长度,编写转换的接口。
由于bmp图像大多未经压缩,不同于jpeg的复杂压缩算法,直接从内存中读取bmp图片即可完成转换。首先了解bmp文件格式。
bmp文件由以下部分组成,1、文件信息头,2、bmp信息头,3、调色板(可选),4、图像数据。对于调色板,仅在深度为8时存在(每个像素有256种表示),当今的bmp图像色深普遍为24或32bit,3通道或4通道,因此大多无调色板。存在调色板的图片,其图像数据中每个像素为调色板的索引,因此处理调色板图片时,需要把索引值转换为标准像素值。由于bmp图像大多无调色板,因此该程序没有处理调色板特殊情况,同时也没有处理存在压缩的情况。
在网上找到一个IplImage和bmp相互转换的例子,是基于类对象封装的,但不够简洁,就编写了如下代码。
在工程中首先定义bmp文件头和信息头,注意内存对齐。
//文件头结构体
#pragma pack(push)
#pragma pack(2) //两字节对齐,否则bmp_fileheader会占16Byte
//bmp文件头
struct bmp_fileheader
{
unsigned short bfType; //文件标识
unsigned long bfSize; //文件大小
unsigned long bfReverved1; //保留
unsigned long bfOffBits;//数据偏移
};
//bmp信息头
struct bmp_infoheader
{
unsigned long biSize;//信息头长度
unsigned long biWidth;//宽度
unsigned long biHeight;//高度
unsigned short biPlanes;//柱面数=1
unsigned short biBitCount;//像素位数
unsigned long biCompression;//压缩说明
unsigned long biSizeImage;//位图数据的大小
unsigned long biXPelsPerMeter;//水平分辨率
unsigned long biYpelsPerMeter;//垂直分辨率
unsigned long biClrUsed;//使用的颜色数
unsigned long biClrImportant;//重要的颜色数
};
#pragma pack(pop)
其中部分参照了bmp文件格式说明。
定义基本格式后,就要对内存中的图像进行读取转换,定义如下函数:
IplImage *BMP2Ipl(unsigned char *src, int FileSize)
{
bmp_fileheader *Fileheader = (bmp_fileheader *)src;
//检查错误
assert(FileSize == Fileheader->bfSize);
//判断是否是位图
if(Fileheader->bfType!= 0x4d42)
return NULL;
//开始处理信息头
bmp_infoheader *bmpheader = (bmp_infoheader *)(src + sizeof(bmp_fileheader));
unsigned int width = bmpheader->biWidth;//宽度
unsigned int height = bmpheader->biHeight;//高度
unsigned int hSize = bmpheader->biSize;//信息头长度
if(bmpheader->biBitCount < 24)
return NULL;//支持真色彩32位
if(bmpheader->biCompression != 0)
return NULL;//不支持压缩算法
//数据大小
unsigned int dataByteSize = bmpheader->biSizeImage;//实际位图数据大小
unsigned int rowByteSize = dataByteSize/height;//对齐的每行数据
IplImage *img = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, bmpheader->biBitCount/8);
//拷贝实际数据无颜色表
unsigned char *gc = src + FileSize - rowByteSize;
char *p = img->imageData;
for(int i=0; i<height; ++i)
{
memcpy(p, gc, rowByteSize);
p += rowByteSize;
gc -= rowByteSize;
}/**/
通道数处理
//for(int i=0;i<height;++i)
//{
// for(int j=0;j<width;++j)
// {
// *(p+3*j+0) = *(gc+3*j+0);//通道0
// *(p+3*j+1) = *(gc+3*j+1);//通道1
// *(p+3*j+2) = *(gc+3*j+2);//通道2
// }
// p += rowByteSize;
// gc -= rowByteSize;
//}
return img;
}
该函数不支持压缩的图片,由于bmp图像数据是倒置从文件尾部逐行存储,因此转换过程中需要倒置数据。
转换后的IplImage直接拿来用就可以了。