音视频入门-03-RGB转成BMP图片
BMP 文件格式解析
BMP 文件由文件头、位图信息头、颜色信息和图形数据四部分组成。
| 位图文件头(14个字节) | 位图信息头(40个字节) | 颜色信息 | 图形数据 |
|---|
- 文件头与信息头一共是 54 字节
- RGB 数据部分:
RGB24 文件存储的顺序是 RGB, RGB, RGB ...... RGB
BMP 文件 RGB 数据存储的顺序是 BGR, BGR, BGR ... BGR
位图文件头
位图文件头分 4 部分,共 14 字节
| 名称 | 占用空间 | 内容 | 示例数据 |
|---|---|---|---|
| bfType | 2字节 | 标识,就是“BM” | BM |
| bfSize | 4字节 | 整个 BMP 文件的大小 | 0x000C0036(786486) |
| bfReserved1 | 2字节 | 保留字 | 0 |
| bfReserved2 | 2字节 | 保留字 | 0 |
| bfOffBits | 4字节 | 偏移数,即位图文件头+位图信息头+调色板的大小 | 0x36(54) |
位图信息头
位图信息头共 40 字节
| 名称 | 占用空间 | 内容 | 示例数据 |
|---|---|---|---|
| biSize | 4字节 | 位图信息头的大小,为40 | 0x28(40) |
| biWidth | 4字节 | 位图的宽度,单位是像素 | 0x200(512) |
| biHeight | 4字节 | 位图的高度,单位是像素 | 0x200(512) |
| biPlanes | 2字节 | 固定值1 | 1 |
| biBitCount | 2字节 | 每个像素的位数 1-黑白图,4-16色,8-256色,24-真彩色,32-带 alpha 通道 | 0x18(24) |
| biCompression | 4字节 | 压缩方式,BI_RGB(0)为不压缩 | 0 |
| biSizeImage | 4字节 | 位图全部像素占用的字节数,BI_RGB时可设为0 | 0x0C |
| biXPelsPerMeter | 4字节 | 水平分辨率(像素/米) | 0 |
| biYPelsPerMeter | 4字节 | 垂直分辨率(像素/米) | 0 |
| biClrUsed | 4字节 | 位图使用的颜色数 如果为0,则颜色数为2的biBitCount次方 | 0 |
| biClrImportant | 4字节 | 重要的颜色数,0代表所有颜色都重要 | 0 |
将 RGB24 像素点数据转成 BMP 格式图片
转换代码:
#include <stdio.h>
#include <stdlib.h>
// 彩虹的七种颜色
u_int32_t rainbowColors[] = {
0XFF0000, // 红
0XFFA500, // 橙
0XFFFF00, // 黄
0X00FF00, // 绿
0X007FFF, // 青
0X0000FF, // 蓝
0X8B00FF // 紫
};
/*bmp file header*/
typedef struct {
unsigned int bfSize; /* Size of file */
unsigned short bfReserved1; /* Reserved */
unsigned short bfReserved2; /* ... */
unsigned int bfOffBits; /* Offset to bitmap data */
} BitmapFileHeader;
/*bmp info header*/
typedef struct {
unsigned int biSize; /* Size of info header */
int biWidth; /* Width of image */
int biHeight; /* Height of image */
unsigned short biPlanes; /* Number of color planes */
unsigned short biBitCount; /* Number of bits per pixel */
unsigned int biCompression; /* Type of compression to use */
unsigned int biSizeImage; /* Size of image data */
int biXPelsPerMeter; /* X pixels per meter */
int biYPelsPerMeter; /* Y pixels per meter */
unsigned int biClrUsed; /* Number of colors used */
unsigned int biClrImportant; /* Number of important colors */
} BitmapInfoHeader;
void writeRGBToBmp(char *filename, int width, int height) {
FILE *bitmapFile = fopen(filename, "wb");
if(!bitmapFile) {
printf("Could not write file \n");
return;
}
uint16_t bfType = 0x4d42;
BitmapFileHeader fileHeader;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfSize = 2 + sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + width*height*3;
fileHeader.bfOffBits = 0x36;
BitmapInfoHeader infoHeader;
infoHeader.biSize = sizeof(BitmapInfoHeader);
infoHeader.biWidth = width;
infoHeader.biHeight = height;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 24;
infoHeader.biSizeImage = 0;
infoHeader.biCompression = 0;
infoHeader.biXPelsPerMeter = 5000;
infoHeader.biYPelsPerMeter = 5000;
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0;
fwrite(&bfType, sizeof(bfType), 1, bitmapFile);
fwrite(&fileHeader, sizeof(fileHeader), 1, bitmapFile);
fwrite(&infoHeader, sizeof(infoHeader), 1, bitmapFile);
// 写入图像数据
for (int i = 0; i < width; ++i) {
// 当前颜色
u_int32_t currentColor = rainbowColors[0];
if(i < 100) {
currentColor = rainbowColors[0];
} else if(i < 200) {
currentColor = rainbowColors[1];
} else if(i < 300) {
currentColor = rainbowColors[2];
} else if(i < 400) {
currentColor = rainbowColors[3];
} else if(i < 500) {
currentColor = rainbowColors[4];
} else if(i < 600) {
currentColor = rainbowColors[5];
} else if(i < 700) {
currentColor = rainbowColors[6];
}
// 当前颜色 R 分量
u_int8_t R = (currentColor & 0xFF0000) >> 16;
// 当前颜色 G 分量
u_int8_t G = (currentColor & 0x00FF00) >> 8;
// 当前颜色 B 分量
u_int8_t B = currentColor & 0x0000FF;
for (int j = 0; j < height; ++j) {
// 按 BGR 顺序写入一个像素 RGB24 到文件中
fwrite(&B, 1, 1, bitmapFile);
fwrite(&G, 1, 1, bitmapFile);
fwrite(&R, 1, 1, bitmapFile);
}
}
// 关闭文件
fclose(bitmapFile);
}
int main() {
writeRGBToBmp("/Users/staff/Desktop/rainbow-700x700.bmp", 700, 700);
return 0;
}
检查生成的 BMP 图片
Congratulations! 图片查看软件识别出我们的 BMP 图片了,预览正常!
BUT!好像哪里不对劲?!我们的彩虹倒过来了!
彩虹的颜色,从上到下应该是:
红 -> 橙 -> 黄 -> 绿 -> 青 -> 蓝 -> 紫
这张图是:
紫 -> 蓝 -> 青 -> 绿 -> 黄 -> 橙 -> 红

处理图片倒立问题
BitmapInfoHeader 中的 biHeight 字段,
biHeight 为正,位图自底向顶扫描,
biHeight 为负,位图自顶向底扫描。
如果这个值的设置和原始位图文件扫描方式不符,则图像显示可能会颠倒。
将上面的转换代码中,BitmapInfoHeader 部分:
// infoHeader.biHeight = height;
infoHeader.biHeight = -height;

Congratulations!
成功用像素点拼出了一张 “真正” 的图片!
代码:
参考资料:
non-dword-aligned-pixel-to-dword-aligned-bitmap
generate-bmp-file-from-array-of-rgb-values
内容有误?联系作者:

音视频入门-03-RGB转成BMP图片的更多相关文章
- 音视频入门-19-使用giflib处理GIF图片
* 音视频入门文章目录 * GIFLIB The GIFLIB project 上一篇 [手动生成一张GIF图片], 自己生成了一张 GIF 动态图 rainbow.gif. 下面,使用 GIFLIB ...
- 音视频入门-20-BMP、PNG、JPG、GIF静态图生成GIF动态图
* 音视频入门文章目录 * 静态图 -> 动态图 前面 [18-手动生成一张GIF图片] 和 [19-使用giflib处理GIF图片] 生成的 GIF 每一帧都是一个颜色,平时用到的 GIF 每 ...
- 音视频入门-01-认识RGB
* 音视频入门文章目录 * RGB 简介 RGB 色彩模式是工业界的一种颜色标准,是通过对红(R).绿(G).蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB 即是代表红 ...
- 音视频入门-10-使用libyuv对YUV数据进行缩放、旋转、镜像、裁剪、混合
* 音视频入门文章目录 * libyuv libyuv 是 Google 开源的实现各种 YUV 与 RGB 之间相互转换.旋转.缩放等的库.它是跨平台的,可在 Windows.Linux.Mac.A ...
- 音视频入门-09-RGB&YUV互转-使用开源库
* 音视频入门文章目录 * 介绍开源库 使用第三方开源库来简化开发,屏蔽一些底层的复杂度,节省大量编写代码的时间. libyuv: Google 开源的实现各种 YUV 与 RGB 之间相互转换.旋转 ...
- 音视频入门-08-RGB&YUV
* 音视频入门文章目录 * YUV & RGB 相互转换公式 YCbCr 的 Y 与 YUV 中的 Y 含义一致,Cb 和 Cr 与 UV 同样都指色彩,Cb 指蓝色色度,Cr 指红色色度,在 ...
- 音视频入门-05-RGB-TO-BMP使用开源库
* 音视频入门文章目录 * RGB-TO-BMP 回顾 将 RGB 数据转成 BMP 图片: 了解 BMP 文件格式 准备 BMP 文件头信息 准备 BMP 信息头 BMP 存储 RGB 的顺序是 B ...
- 音视频入门-04-BMP图像四字节对齐的问题
* 音视频入门文章目录 * BMP 图像四字节对齐 表示 BMP 位图中像素的位元是以行为单位对齐存储的,每一行的大小都向上取整为4字节(32 位 DWORD)的倍数.如果图像的高度大于 1,多个经过 ...
- 音视频入门-11-PNG文件格式详解
* 音视频入门文章目录 * PNG 文件格式解析 PNG 图像格式文件由一个 8 字节的 PNG 文件署名域和 3 个以上的后续数据块(IHDR.IDAT.IEND)组成. PNG 文件包括 8 字节 ...
随机推荐
- Oracle sql 复习题目总结
sql 题目一 表结构 1.表名:g_cardapply 字段(字段名/类型/长度): apply_no varchar8; //申请单号(关键字) apply_date date; //申请日期 s ...
- 如何计算一个C/C++程序运行时间
前两天要计算一个用C++实现的算法运行时间,就用了clock()这个函数.程序大体上如下: clock_t start,end; start = clock(); /*my code*/ end = ...
- 为什么ROC曲线不受样本不均衡问题的影响
转自:https://blog.csdn.net/songyunli1111/article/details/82285266 在对分类模型的评价标准中,除了常用的错误率,精确率,召回率和F1度量外, ...
- OpenTK学习笔记(2)-工作窗口的三种方法创建方法(winfrom窗体控件形式创建)
参考资料: https://social.msdn.microsoft.com/Forums/zh-TW/1b781685-c670-4338-953d-1957a8f24a66/opentkglco ...
- Flex 布局的最简单表单
http://www.ruanyifeng.com/blog/2018/10/flexbox-form.html https://www.cnblogs.com/grt322/p/8531882.ht ...
- java如何生成 MD5?
java生成 MD5最简单方法 答: 复制以下方法即可使用,不用依赖任何第三方包 public static String MD5(String data) throws Exception { ...
- sqlmap开源 测试sql注入的工具 各种参考链接
https://www.cnblogs.com/insane-Mr-Li/p/10150165.html https://github.com/sqlmapproject/sqlmap 官网 http ...
- 使用 ServiceStack.Text 序列化 json
相信做 .net 开发的朋友经常会遇到 json 序列化这样的需要,今天发篇文章总结下自己使用 ServiceStack.Text 来序列化 json.它的速度比 Newtonsoft.Json 快很 ...
- 【ARTS】01_38_左耳听风-201900729~201900804
ARTS: Algrothm: leetcode算法题目 Review: 阅读并且点评一篇英文技术文章 Tip/Techni: 学习一个技术技巧 Share: 分享一篇有观点和思考的技术文章 Algo ...
- Django:reverse反转URL并传递参数
需求: 假设在文章详情页评论文章后需要重新刷新显示该页面(原始方法,提交评论表单为form方式,未采用ajax方式), 提交评论后代码会走comment的视图函数,等数据入库之后需要将页面重新定位到文 ...