RGB24转换成BMP图像

最近学习雷神的博客, RGB24转换成BMP位图, 发现一些问题与心得, 记录下来

1.位图知识:(会列出两篇比较好的博文, 这里简单总结一下)

位图结构:

位图文件头(14字节)

位图信息头(40字节)

彩色表(RGB24真彩不存在彩色表)

位图数据(实际上就是RGB数据)

【注意1】:位图文件头与位图信息头一共是54字节, 雷神在博文里写的结构体不正确, 正确的结构体如下:

贴图中的位图文件头为12字节, 因为有两个字节的BM用字符数组表示了, 详见最后的完整代码

/*12Bytes*/
typedef struct                       /**** BMP file header structure ****/  
{  
	unsigned int   bfSize;           /* Size of file */  
	unsigned short bfReserved1;      /* Reserved */  
	unsigned short bfReserved2;      /* ... */  
	unsigned int   bfOffBits;        /* Offset to bitmap data */  
}BITMAPFILEHEADER;

/*40Bytes*/
typedef struct                       /**** BMP file info structure ****/  
{  
	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;

【注意2】:BMP的存储方式是小端存储, 即低字节放低地址, 高字节放高地址

关于大小端存储, 我一直有误区, 我错误理解为"地位放在低地址, 高位放在高地址",这是不对的, 应该是将整个低字节的内容都存储到低地址去

例如内存中存储0x12345678的小端存法应该是0x78 0x56 0x34 0x12(可以写代码进行验证)

关于大小端, 比较好的博客是:

https://blog.csdn.net/ce123_zhouwei/article/details/6971544

【注意3】:C语言结构体对齐的相关知识《C语言结构体对齐》

rules one:数据成员对齐规则:

结构(struct)或联合(union)的数据成员, 第一个数据成员放在offset为0的地方, 以后每个数据成员存储的起始位置要从该成员大小的整数倍开始

rules two:

结构体的总大小, 也就是sizeof的结果, 必须其内部最大成员的整数倍, 不足的要补齐

【注意4】:BMP图像的RGB数据存储方式

part1:RGB文件存储性质-大端存储

我个人理解RGB文件的存储方式是大端性质的, 即低地址存高字节, 高地址存低字节

因为从RGB文件读出来的数据可知是按照R-G-B R-G-B R-G-B顺序的, 而在RGB编码中R为高字节, B为低字节

所以在BMP存储时要将R和B的顺序调换, 方法如下:

/*change R-G-B to B-G-R*/
for(i = 0; i < (w*h); i++)
{
	temp = *(readBuff + i*3);
	*(readBuff + i*3) = *(readBuff + i*3 + 2);
	*(readBuff + i*3 + 2) = temp;	
}

part2:BMP文件数据存储方式

若BITMAPINFOHEADER的成员biHeight为负值, 则BMP数据存储方向为从左到右, 从上到下

若BITMAPINFOHEADER的成员biHeight为正值, 则BMP数据存储方向为从左到右, 从下到上

我在程序中写入宏#define POSITIVE_HEIGHT,并将biHeight的值填为正数

如上四点, 比较好的博文为:【转】

https://www.cnblogs.com/liulijin/p/9108099.html

http://wojiaolongyinong.iteye.com/blog/1896092

https://blog.csdn.net/mlfcjob/article/details/78918235

https://blog.csdn.net/ce123_zhouwei/article/details/6971544

2.RGB24 to BMP代码

#include "common_head.h"

#define MAX_LEN (1*1024*1024)
#define POSITIVE_HEIGHT (1)

/*12Bytes*/
typedef struct                       /**** BMP file header structure ****/  
{  
	unsigned int   bfSize;           /* Size of file */  
	unsigned short bfReserved1;      /* Reserved */  
	unsigned short bfReserved2;      /* ... */  
	unsigned int   bfOffBits;        /* Offset to bitmap data */  
}BITMAPFILEHEADER;

/*40Bytes*/
typedef struct                       /**** BMP file info structure ****/  
{  
	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;  

HI_S32 simplest_rgb24_to_bmp(const char* rgb24Path, int w, int h, const char* bmpPath)
{
	HI_S32 s32Ret = 0;
	int fd_ori = -1;
	int fd_bmp = -1;
	int headerSize = 0;
	int i = 0;//for circle
	int j = 0;//for circle
	unsigned char temp = 0;

	unsigned char readBuff[MAX_LEN] = {'\0'};
	memset(readBuff, 0, sizeof(readBuff));

#ifdef POSITIVE_HEIGHT
	unsigned char readBuff4Ph[MAX_LEN] = {'\0'};
	memset(readBuff4Ph, 0, sizeof(readBuff4Ph));
#endif

	char bfType[2] = {'B', 'M'};

	BITMAPFILEHEADER myHead;
	BITMAPINFOHEADER myHeadInfo;
	memset(&myHead, 0, sizeof(myHead));
	memset(&myHeadInfo, 0, sizeof(myHeadInfo));
	printf("sizeof(myHead) = %d\n", sizeof(myHead));
	printf("sizeof(myHeadInfo) = %d\n", sizeof(myHeadInfo));

	/*myHead*/
	headerSize = sizeof(bfType) + sizeof(myHead) + sizeof(myHeadInfo);
	myHead.bfSize = headerSize + w*h*3;
	myHead.bfOffBits = headerSize;

	/*myHeadInfo*/
	myHeadInfo.biSize = sizeof(myHeadInfo);
	myHeadInfo.biWidth = w;

#ifndef POSITIVE_HEIGHT
	myHeadInfo.biHeight = -1 * h;
#else
	myHeadInfo.biHeight = h;
#endif

	myHeadInfo.biPlanes = 1;
	myHeadInfo.biBitCount = 24;
	myHeadInfo.biSizeImage = w*h*3;

	/*open files*/
	fd_ori = open(rgb24Path, O_RDONLY);
	if(fd_ori < 0)
	{
		printf("open rgb24 failed!\n");
		return -1;
	}
	printf("open rgb24 success!\n");

	fd_bmp = open(bmpPath, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 777);
	if(fd_bmp < 0)
	{
		printf("open bmp failed!\n");
		close(fd_ori);
		return -1;
	}
	printf("open bmp success!\n");

	/*read*/
	memset(readBuff, 0, sizeof(readBuff));
	s32Ret = read(fd_ori, readBuff, sizeof(readBuff));
	if((s32Ret < 0) || (s32Ret != w*h*3))
	{
		printf("read RGB file failed!\n");
		close(fd_bmp);
		close(fd_ori);
		return -1;
	}
	printf("read RGB file success!\n");

	/*change R-G-B to B-G-R*/
	for(i = 0; i < (w*h); i++)
	{
		temp = *(readBuff + i*3);
		*(readBuff + i*3) = *(readBuff + i*3 + 2);
		*(readBuff + i*3 + 2) = temp;	
	}

/*positive height storage sequence:left-right down-up*/
#ifdef POSITIVE_HEIGHT
	for(i = (h - 1), j = 0; i >= 0; i--, j++)
	{
		memcpy(readBuff4Ph + j*w*3, readBuff + i*w*3, w*3);
	}
#endif

	/*write-4 parts*/
	s32Ret = write(fd_bmp, bfType, sizeof(bfType));
	if(s32Ret < 0)
	{
		printf("write bfType failed!\n");
		close(fd_bmp);
		close(fd_ori);
		return -1;
	}
	s32Ret = write(fd_bmp, &myHead, sizeof(myHead));
	if(s32Ret < 0)
	{
		printf("write myHead failed!\n");
		close(fd_bmp);
		close(fd_ori);
		return -1;
	}
	s32Ret = write(fd_bmp, &myHeadInfo, sizeof(myHeadInfo));
	if(s32Ret < 0)
	{
		printf("write myHeadInfo failed!\n");
		close(fd_bmp);
		close(fd_ori);
		return -1;
	}
#ifdef POSITIVE_HEIGHT
	s32Ret = write(fd_bmp, readBuff4Ph, w*h*3);
	if(s32Ret < 0)
	{
		printf("write readBuff4Ph failed!\n");
		close(fd_bmp);
		close(fd_ori);
		return -1;
	}
	printf("write readBuff4Ph success!\n");
#else
	s32Ret = write(fd_bmp, readBuff, w*h*3);
	if(s32Ret < 0)
	{
		printf("write readBuff failed!\n");
		close(fd_bmp);
		close(fd_ori);
		return -1;
	}
	printf("write readBuff success!\n");
#endif

	close(fd_bmp);
	close(fd_ori);
	return 0;
}

最后得到的BMP图为:(CSDN传不上, 只有截图一下了)

猜你喜欢

转载自blog.csdn.net/liaojunwu/article/details/82285295