0.前言
目录
0.前言
1.BMP格式详解
1.1图片的构成
1.2BMP格式
1.2.1文件头
1.2.2位图信息头
1.2.3调色板
1.2.4位图数据
2.生成
3.总结
最近工作任务繁重且对我来说小有难度,但是没有困难的事情做起来怎么有收获呢?
今天推荐一本书《寻找光的小女孩》,挺感人的。记得是高中借的同学的看的,刚开始看到书名感觉不怎么投我的胃口,果不其然不可“以貌取书”,熬夜看完了。
最近工作上逐渐步入正轨,研发的工作嘛,对吧,不可以设置限制,这才能体现到研发,才能发挥研发的最大福利。工作也算是小半年了,逐渐慢慢对手里的东西或者说项目有了自己的认识,这或许就是经验。这种感觉我刚刚能觉察到,相信再给我一些时间,可以掌握吧。
喜欢上了喝无糖可乐。哈。
1.BMP格式详解
这是最近工作的内容,任务是从csv文件获取数据,将获取到的数据通过算法转为灰度值,然后填充进BMP格式图片,要求图片位深16位。
1.1图片的构成
其实我们知道每张图片都是由每个像素组成的,多少的像素也构成图片的大小。每个像素是什么构成的呢?在bmp中就是数值。在此之前,先说明两个概念,位深和色深。(好长时间不写博客,手生了起来,逻辑思路也不如以前,见谅)。
位深:这个名词是用来描述什么?每个像素的大小,是一个大小的代名词。比如位深是24的图片,就是说这张图片的每个像素的值是由24比特的大小来表示,比如一幅图片是1080*1280的大小,那么位深24的bmp大小就在1080*1280*3字节左右(这里我是在Linux下做的,从虚拟机拷贝到win下大小会不同,大概是因为架构的不同)。
色深:在bmp中,每个像素的值,其实也就是颜色的值是由三通道RGB组成的,24位的图,每个通道的表示为8比特,即一字节,范围是0-255,三通道(红绿蓝)范围皆是0-255,那这三种通道合成的颜色也就是255*255*255种,这个值就叫做色深,是一个数量的范围,描述颜色的多少。
这两种概念容易模糊,不知道我写的能不能帮助大家理解。
1.2BMP格式
参考:bmp格式andbmp格式
就把这个图片想象成普通的文件就行了,只不过区别就是要有固定的头。
1.2.1文件头
文件头,顾名思义是描述这个文件的信息,也就是这个图片的信息。这里面的内容包含了文件的大小这一重要信息。此部分大小是14字节。
字节顺序 | 数据结构 | 描述 |
1,2 | 16比特 | 高8位位字母‘B,低8位为字母’M‘ |
3,4,5,6 | 32 | 文件尺寸 |
7,8 | 16 | 保留字1 |
9,10 | 16 | 保留字1 |
11,12,13,14 | 32 | 位图数据部分相对于文件的起始偏移量 |
数据部分偏移量的存在,说明图像数据部分并不一定要紧随图像参数或调色板之后放置,BMP图片的制作者其实可以在调色板之后、数据部分之前填充任何内容,只要正确地设置偏移量即可。
1.2.2位图信息头
此部分用来描述图片的信息,比如图像宽高、bpp等。此部分大小是40字节。
字节顺序 | 数据结构 | 描述 |
15,16,17,18 | uint | 当前结构体的大小,通常是40或56 |
19,20,21,22 | int | 图像宽度(像素) |
23,24,25,26 | int | 图像高度(像素) |
27,28 | word | 恒为1 |
29,30 | word | 每个像素占用的位数即bpp |
31,32,33,34 | uint | 压缩方式 |
35,36,37,38 | uint | 图像的尺寸 |
39,40,41,42 | int | 水平分辨率 |
43,44,45,46 | int | 垂直分辨率 |
47,48,49,50 | uint | 引用色彩数 |
51,52,53,54 | uint | 关键色彩数 |
31-34字节表示图像数据的压缩方式,参数取值范围是0,1,2,3等。
0 ----------------RGB方式
1 --------------- 8bpp的run-length-encoding方式
2 --------------- 4bpp的run-length-encoding方式
3 ---------------- bit-fields方式
只有压缩方式选项被设置为bit-fileds时,当前结构体大小为56字节,否则,为40字节。
1.2.3调色板
我们一般见到的图像以24位图像为主,即R、G、B三种颜色各用8个bit来表示,这样的图像我们称为真彩色,这种情况下是不需要调色板的,也就是所位图信息头后面紧跟的就是位图数据了。这部分我略过,有兴趣的同学可以自己研究一下。
1.2.4位图数据
就是每个像素的值,位图数据,每个像素占一个字节。24位数据按照BGR填充,例如一张2个像素的bmp24位图,它的构成就是:14字节文件头信息,40字节位图信息,2个像素,每个像素大小是24位,也就是3字节,第一个像素的blue就是8比特,后面紧跟8比特green,再是8比特red,后一个像素也是如此。一共14+40+2*3=60字节。那这个2个像素的bmp文件就占大小60字节。(不涉及调色板)
后面以详细代码说明。
2.生成
代码部分我只给出生成的,完整代码大家可以下载:完整代码。注意,此代码是从csv文件获取数据、分割数据并提取。
int bmp_gen_pic(char *fileName, uint32_t width, uint32_t height, unsigned char *color)
{
// width = 2048 height = 512 color = data_pic
#if 1
FILE *fp;
uint32_t i, j;
LITTLE l_width, l_height, l_bfSize, l_biSizeImage;
uint8_t r = color[0];
uint8_t g = color[0];
uint8_t b = color[0];
uint32_t width_r = (width * 24 / 8 + 3) / 4 * 4; // width_r 此变量代表的是每行的字节数 6144(整形) 这步保证宽度是4的倍数(bmp格式要求)
// printf("width_r = %ld\n",width_r);
uint32_t bfSize = width_r * height + 54 + 2; //此变量代表生成的图片的大小
uint32_t biSizeImage = width_r * height; //此变量代表图片的像素大小
// printf("hight = %ld\n",height);
// printf("size = %ld\n",biSizeImage);
l_width.value = width; // 2048
l_height.value = height; // 512
l_bfSize.value = bfSize; // 3145784
l_biSizeImage.value = biSizeImage; // 3145728
/* BMP file format: www.cnblogs.com/wainiwann/p/7086844.html */
// bmp格式头 固定
uint8_t bmp_head_map[54] = {
//这个头文件 包含两部分 一是文件头 14 二是位图信息头 40
/* bmp file header: 14 byte */
0x42, 0x4d, // 42 代表 B 4d代表M
// bmp pixel size: width * height * 3 + 54
l_bfSize.bytes[0], l_bfSize.bytes[1], l_bfSize.bytes[2], l_bfSize.bytes[3], //代表文件的尺寸
0, 0, 0, 0,
54, 0, 0, 0, /* 14+40=54 */ //位图部分相对文件的起始偏移量 就是多少字节后是位图数据(像素值)
/**************************************************************/
/* bmp map info: 40 byte */
40, 0, 0, 0, //当前的大小 就是40 意思后面40都是描述本部分
// width 图像宽度(像素)
l_width.bytes[0], l_width.bytes[1], l_width.bytes[2], l_width.bytes[3],
// height 图像高度(像素)
l_height.bytes[0], l_height.bytes[1], l_height.bytes[2], l_height.bytes[3],
1, 0, //恒为1
24, 0, //每个像素占用的位数(bpp) /* 24 bit: R[8]/G[8]/B[8] */
0, 0, 0, 0, // biCompression:0 压缩方式
//图像的尺寸
l_biSizeImage.bytes[0], l_biSizeImage.bytes[1], l_biSizeImage.bytes[2], l_biSizeImage.bytes[3],
0, 0, 0, 0, //水平分辨率 //biXPelsPerMeter: 60 0F 00 00
0, 0, 0, 0, //垂直分辨率 //biYPelsPerMeter
0, 0, 0, 0, //引用色彩数 //biClrUsed
0, 0, 0, 0 //关键色彩数 //biClrImportant
};
/* write in binary format */
fp = fopen(fileName, "wb+"); // wb+代表打开一个读写打开一个二进制文件
if (fp == NULL)
{
printf("%s: file create failed!\n", fileName);
return -1;
}
printf("%s: file create success!\n", fileName);
fwrite(bmp_head_map, sizeof(bmp_head_map), 1, fp); //写入1个sizeof(头)的大小
uint8_t databmp[KUAN*GAO*3];
memset(databmp, 0, sizeof(databmp));
uint32_t pos = 0;
#if 1
for (i = 0; i < height; i++)
{ // 512
for (j = 0; j < width; j++) // 2048
{
//fprintf(fp, "%c%c%c", color[j + i * width], color[j + i * width], color[j + i * width]); /* BGR */ // FF FF FF 白色 00 00 00 黑色
databmp[pos++] = color[j + i * width];
databmp[pos++] = color[j + i * width];
databmp[pos++] = color[j + i * width];
}
}
fwrite(databmp, sizeof(databmp), 1, fp);
#endif
fprintf(fp, "%c%c", 0, 0); // PhotoShop two byte "0"
if (fclose(fp))
{
printf("file close failed!\n");
return -1;
}
fp = NULL;
return 0;
#endif
}
代码我注解的很清楚,不懂可以留言。
3.总结
好家伙,写了一个星期,shit.
工作忙起来对博客是疏忽很多,见谅。
上班时间消耗完了所有精力,累到不是累,只是想下班后不再想动脑,去获得简单纯粹的娱乐。
生活需要新鲜,Everything is like this.
最后再说一点,觉得难不要陷入“想”的境地,要去做。走过来后就会发现,也是如此。