设备无关位图(Device Independent Bitmap)是可以保存在磁盘的位图文件,可以从磁盘读取到内存或者从内存保存到磁盘上。它的文件结构是标准化的,可以在Windows/Linux/Unix等平台上显示相同的效果。本文主要介绍了

  1. 如果将位图文件从磁盘读到内存中
  2. 在内存中对位图文件进行操作后,如何将位图保存到磁盘

1 读取位图到内存中

1.1 DIB文件结构

要将位图文件(.bmp)从磁盘读取到内存,首先要了解其文件结构。DIB的文件组成有以下4个部分:

  1. 文件表头,主要包含了文件的类型(必须是BM),文件的大小(所占用的字节数)和位图的像素矩阵的便宜量。
  2. 信息表头,包含了两部分内容:位图的相关信息(位图的大小、位深度、位面数、压缩和编码等)和指向RGB颜色表(调色盘)的指针。
  3. RGB色彩对照表,也就是调色板,不一定会有。16位及以上直接使用RGB通道表示颜色,一般不需要调色板。
  4. 位图的像素信息矩阵,表示具体的像素。1,4,8位颜色,保存的是调色板的索引,具体的颜色根据索引在调色板中查找;16位及其以上不使用调色板,直接使用RGB组成像素颜色。

1.2 在Windows下DIB的内存结构

要将DIB数据读取到内存,就需要在内存中分配相应的空间。Windows提供了几种结构体,结构体中的字段对应着DIB文件的各个信息值,具体如下

引用自 http://blog.csdn.net/wenzhou1219/article/details/26162869

将DIB读取到内存只需要将磁盘数据填充到相应到结构体即可。在磁盘上DIB需要连续的结构存储,在内存中则不需要连续的存储空间,可以分段将数据读取到相应的结构体中。

读取DIB到内存的具体步骤:

  1. 将文件头信息读取到BITMAPFILEHEADER结构体中。
  2. 将位图头信息读取到BITMAPINFOHEADER结构体中。
  3. 如果有调色板,则将其信息读取到RGBQUAD中。
  4. 读取位图像素信息到像素矩阵中。
fp.Read(&bmfileHeader, sizeof(BITMAPFILEHEADER)); // 读取BMP文件头
...
//读取文件信息头
ret = fp.Read(&bmHeader, sizeof(BITMAPINFOHEADER));
...
fp.Read(m_dibBits, GetBodySize()); //读取像素信息

1.3 结构体各字段信息

BITMAPFILEHEADER

代表文件头信息的结构体BITMAPFILEHEADER的声明如下:

typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER

其中,

  • bfType是文件类型,该字段必须是BM,如果不是则说明该文件不是DIB。
  • bfSize是位图文件的大小(字节数)
  • bfReserved1和bfReserved2 是保留字段
  • bfOffBits 从BITMAPFILEHEADER的起始位置到位图像素的字节偏移量。
BITMAPINFO

在上面提到,位图信息和位图的调色板是存放在同一个结构体中的,该结构体就是BITMAPINFO,其声明如下

typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO, *PBITMAPINFO;

bmiHeader是位图头信息

bmiColors是调色板

BITMAPINFOHEADER

位图的头信息结构BITMAPINFOHEADER,该结构包含了DIB的尺寸和颜色格式等信息,声明如下

typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER

其中,

  • biSize是该结构体所占用的字节说
  • biWidth DIB的宽(以像素为单位),如果biCompression是BI_JPEG或者BI_PNG,则biWidth是解压缩后JPEG或者PNG图像的宽度。
  • biHeight,DIB的高(以像素为单位)。
    • 如果biHeight是正的,则DIB的像素是按照从下往上(bottom-up,也就是像素数组的第一行保存的实际是DIB的最后一行像素值),图像源点在左下角。
    • 如果biHeight是负的,则DIB的像素是按照从上往下保存的(up-bottom),而且像素的数据是不能被压缩的其biCompression必须是BI_RGB或者BI_FIELDS
    • 如果biCompression是BI_JPEG或者BI_PNG,则biHeight是解压缩后JPEG或者PNG的高
  • biPlanes 目标设备的平面数,总是设为1.
  • biBitCount,每个像素所占用的位数。0,表示JPEG或者PNG指定每个像素所占用的位数。还可以是1,4,8,16,24,32。
  • biCompression,数据的压缩方法(up-down的DIB不能被压缩),可以是以下值:
    • BI_RGB / BI_FIELDS未被压缩
    • BI_RLE4 / BI_RLE8 使用游程长度编码 (RLE,run-length encode)
    • BI_JPEG / BI_PNG 指示该图像是JPEG或者PNG图像。
  • biSizeImage 图像的字节数,对于BI_RGB的DIB其值为0.
  • biXPelsPerMeter / biYPelsPerMeter 显示该DIB的目标设备所需的分辨率(单位是像素每米)
  • biClrUsed DIB实际使用调色板中的颜色个数,通常为0表示使用调色板中的全部颜色。
  • biClrImportant 显示DIB所必须的颜色个数,通常为0表示全部颜色都是必须的。

详细的解释参见 https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx

RGBQUAD
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;

注意,其存储顺序是BGR。

1.3 DIB的结构实例



是一幅宽和高都是32的位图(放大后,可以看到表示像素的一个个方块),其有16种颜色。下面是该图像的16进制数据

  • 首先是文件的头信息 BITMAPFILEINFO 共有14个字节(0x00-0x0D),其起始的两个字节为0x4d42(大端存储,高位在前)表示文件类型为BM,最后4个字节 0x0076是位图的像素信息相对于文件头的偏移量,也就是从0x0076为图像的像素信息。
  • 下面是位图的头信息 BITMAPINFOHEADER共有40个字节(0x0E-0x35),起始的4个字节是0x0028就是该结构体的大小,紧接着的4个字节是图像的宽0x0020
  • 跟着是图像的调色板,0x36 - 0x75。调色板共有16种颜色,也就是说有调色板中有16个RGBQUAD,其大小为16 * 4.
  • 最后是位图的数据 0x76-0x275

1.4 读取

    CFile fp(dibName, CFile::modeRead | CFile::typeBinary);
BITMAPFILEHEADER bmfileHeader;
BITMAPINFOHEADER bmHeader;
ULONGLONG headpos;
int paletteSize = 0;
int ret, cbHeaderSize;
headpos = fp.GetPosition(); // 获取文件指针的位置
ret = fp.Read(&bmfileHeader, sizeof(BITMAPFILEHEADER)); // 读取BMP文件头
if (bmfileHeader.bfType != 0x4d42) //判断文件类型标头是不是x4d42,表示该文件为BMP类型文件
{
AfxMessageBox(_T("文件不是bmp!"));
return;
}
//读取文件信息头
ret = fp.Read(&bmHeader, sizeof(BITMAPINFOHEADER));
// 计算RGBQUAD的大小
switch (bmHeader.biBitCount)
{
case 1:
paletteSize = 2;
break;
case 4:
paletteSize = 16;
break;
case 8:
paletteSize = 256;
break;
}
// 为BITMAPINFO分配存储空间
cbHeaderSize = sizeof(BITMAPINFOHEADER)+paletteSize * sizeof(RGBQUAD);
m_dibInfo = (BITMAPINFO*) new char[cbHeaderSize];
m_dibInfo->bmiHeader = bmHeader;
if (paletteSize) //是否有调色板
{
ret = fp.Read(&(m_dibInfo->bmiColors[0]), paletteSize * sizeof(RGBQUAD));
if (ret != int(paletteSize * sizeof(RGBQUAD) ) )
{
delete[] m_dibInfo;
m_dibInfo = NULL;
return;
}
}
//为像素数组分配存储空间,大小由GetBodySize决定
m_dibBits = (void*) new char[GetBodySize()];
fp.Seek(headpos + bmfileHeader.bfOffBits, CFile::begin); // 将文件指针移动到DIB像素数组
ret = fp.Read(m_dibBits, GetBodySize());
if (ret != int(GetBodySize()))
{
delete[] m_dibInfo;
delete[] m_dibBits;
m_dibInfo = NULL;
m_dibBits = NULL;
}
fp.Close();

知道了DIB的文件结构后,读取其到内存还是挺简单的,需要注意的是DIB的像素数组的大小。由于DIB的宽度需要时4的倍数,不是的话需要填充0将其凑成4的倍数,所以其像素数组的大小不能简单的width * height * biBitCount / 8,其中biBitCount是每个像素占用的位数。其具体的计算方法如下

  1. 首先计算一行所占用的字节数 bytesPerLine

    bytesPerLine = ((m_dibInfo->bmiHeader.biWidth * m_dibInfo->bmiHeader.biBitCount + 31) / 32 ) * 4
  2. 将bytesPerLine乘以图像的高

    bytesPerLine * m_dibInfo->bmiHeader.biHeight

1.5 总结

本文主要对DIB的文件结构以及其对应的内存中的结构体做了一个总结,并对一个具体的DIB16进制数据结构进行分析,最后实现了如何将一个DIB数据读取到内存中。

一直对位图结构不是很了解,趁着在公司实习没有具体的工作安排,对DIB的结构作了个总结。至于如何将处理后的位图数据写回磁盘文件,在知道位图结构的情况下,只需要填充相应的结构字段就行了,需要注意的还是位图的宽需要是4的倍数,不是的话要用0补齐。本来想写个DEMO的但是太累,明天再说吧。

DIB位图(Bitmap)的读取和保存的更多相关文章

  1. DIB位图文件的格式、读取、保存和显示(转载)

    一.位图文件结构 位图文件由三部分组成:文件头 + 位图信息 + 位图像素数据 1.位图文件头:BitMapFileHeader.位图文件头主要用于识别位图文件.以下是位图文件头结构的定义: type ...

  2. EmguCV从位图(Bitmap)加载Image<Gray,byte>速度慢的问题

    先说背景.最近在用C#+EmguCV(其实就是用P/Invoke封闭了OpecCV,与OpenCVDotNet差不多) 做一个视频的东西.视频是由摄像头采集回来的1f/s,2048X1000大小,其实 ...

  3. Python,ElementTree模块处理XML时注释无法读取和保存的问题

    from xml.etree import ElementTree class CommentedTreeBuilder ( ElementTree.XMLTreeBuilder ): def __i ...

  4. 【原】Learning Spark (Python版) 学习笔记(二)----键值对、数据读取与保存、共享特性

    本来应该上周更新的,结果碰上五一,懒癌发作,就推迟了 = =.以后还是要按时完成任务.废话不多说,第四章-第六章主要讲了三个内容:键值对.数据读取与保存与Spark的两个共享特性(累加器和广播变量). ...

  5. 做参数可以读取参数 保存参数 用xml文件的方式

    做参数可以读取参数 保存参数 用xml文件的方式 好处:供不同用户保存适合自己使用的参数

  6. matlab各格式数据读取与保存函数

    数据处理及matlab的初学者,可能最一开始接触的就是数据的读取与保存: %matlab数据保存与读入 function datepro clear all; %产生随机数据 mat = rand(, ...

  7. IOS webview中cookie的读取与保存-b

    Cookie 的读取 将它放在 webViewDidFinishLoad 开始后执行 NSArray *nCookies = [[NSHTTPCookieStorage sharedHTTPCooki ...

  8. JavaScript进阶(六)用JavaScript读取和保存文件

    用JavaScript读取和保存文件 因为Google还不提供同步插件数据的功能,所以导入和导出插件配置就必须和文件打交道了.而出于安全原因,只有IE才提供访问文件的API:但随着HTML 5的到来, ...

  9. Spark学习之数据读取与保存总结(一)

    一.动机 我们已经学了很多在 Spark 中对已分发的数据执行的操作.到目前为止,所展示的示例都是从本地集合或者普通文件中进行数据读取和保存的.但有时候,数据量可能大到无法放在一台机器中,这时就需要探 ...

随机推荐

  1. 打造AngularJs2.0开发环境

    angularjs2.0刚发布, typescript2.0也刚发布, 于2016.9.29记录. 参考文档:https://angular.cn/docs/ts/latest/quickstart. ...

  2. 浅谈P2P金融

    自从李总理开发互联网大会,提出“互联网+”,好像与互联网相在的所有事情都火起来了.上至80岁的老头,下至十多岁的孩童,都知道了这个词“互联网+”.虽然大家可能对”互联网+“的概念都只是一支半解,但是像 ...

  3. js微博发布框的实现

    观察了微博发布框, 1.发现他的剩余文字是动态改变的, 2.且文字为零时 发布框颜色为暗色 3.文字不符合标准时提交不通过 整理了一下思路 js会主要用到的方法 1.onclick() //点击发布时 ...

  4. Kosaraju 算法检测有向图的强连通性

    给定一个有向图 G = (V, E) ,对于任意一对顶点 u 和 v,有 u --> v 和 v --> u,亦即,顶点 u 和 v 是互相可达的,则说明该图 G 是强连通的(Strong ...

  5. log4j.xml的实用例子

    大多数讲log4j配置的教程用的都是log4j.properties文件,我觉得xml或许更好一点,在这里我提供一个我已经用于生产环境的log4j.xml的例子,先上代码,然后再解释: <?xm ...

  6. [nRF51822] 8、基础实验代码解析大全 · 实验11 - PPI

    前一篇分析了前十个基础实验的代码,从这里开始分析后十个~ 一.PPI原理: PPI(Programmable Peripheral Interconnect),中文翻译为可编程外设互连. 在nRF51 ...

  7. Npm install failed with “cannot run in wd”

    Linux环境下,root账户,安装某些npm包的时候报下面的错误,例如安装grunt-contrib-imagemin时: Error: EACCES, mkdir '/usr/local/lib/ ...

  8. 前端编码规范之JavaScript

    上次浅谈了下关于CSS的编码规范,大部分童鞋持赞同意见,仍存在一些童鞋不太理解这些规范的意义. 如果是个人或者小作坊开发,其实这些所谓的编码规范也没啥意思,因为大家写好的代码直接就给扔到网上去了,很少 ...

  9. rem与px的转换

    rem与px的转换 引用自http://caibaojian.com/rem-and-px.html A-A+ 前端博客•前端开发教程•rem•3702View0 rem是相对于根元素<html ...

  10. angularjs UI Libraries

    angularjs UI Libraries ● ng-bootstrap is currently available. ● PrimeNG has largest number of compon ...