数据压缩5 | 实验2 | bmp文件转yuv

一、内容拆解

  1. 选择的是实验内容①:自行生成多个BMP文件(24bit),至少包含5个不同场景画面,将多个BMP文件转换为YUV文件,并要求在命令行设置每个画面出现的帧数,YUV文件至少包含200帧,调试程序并用播放器观看。

  2. 考察点
    ①BMP文件格式是怎样的,我们转换前要获得哪些数据?=>BMP文件位图大小、每个像素的RGB值及在文件中的排列位置
    ②BMP转YUV有直接的方法吗?没有的话,需要用之前的 RGB作为中介吗?BMP怎么转RGB呢?
    ③怎样在命令行设置画面帧数呢?

二、思路概述

基本思路框图
基本思路框图

1. bmp文件格式分析

BMP(全称 Bitmap)是 Windows 操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用广泛。它采用位映射存储格式,除了图像深度可选以外,在绝大多数应用中不采用其他任何压缩,因此,BMP 文件所占用的空间很大。
BMP 文件的图像深度可选 lbit、4bit、8bit、16bit 及 24bit。BMP 文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。
由于 BMP 文件格式是 Windows 环境中交换与图有关的数据的一种标准,因此在 Windows 环境中运行的图形图像软件都支持 BMP 图像格式。

以0.bmp为例,我们简单看看BMP文件格式:
属性

(1)位图头文件数据结构,占14个字节,它包含 BMP 图像文件的类型、显示内容等信息;位图头文件!(2)位图信息数据结构,占40个字节,它包含有 BMP 图像的宽、高、压缩方法,以及定义颜色等信息;
位图信息数据结构
(3)调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的 BMP)就不需要调色板
(4)位图数据,这部分的内容根据 BMP 位图使用的位数不同而不同,在 24 位图中直接使用 RGB,而其他的小于 24 位的使用调色板中颜色索引值。
位图数据

2. bmp => rgb

过程:读入BMP文件,声明变量,开辟存储空间。从BMP文件头和信息头读入之位图的宽高、bit数等信息,开辟相应空间,定义两个指针:BITMAPFILEHEADER File_header; BITMAPINFOHEADER Info_header来分别用来获取文件头和信息头的数据。这两种类型需要加<windows.h>头文件。从24bit位图数据直接提取出RGB数据,写入缓冲区,图像数据需倒置,否则生成的序列播放将是倒向。

3. yuv序列实现

由RGB转YUV,生成YUV视频循环写入,每张图片素材40帧(后期为了录制较短视频修改为12帧粘贴在下),总共200帧。

三、关键代码

  1. 文件声明部分
#include <stdio.h>
#include<windows.h>
#include<math.h>
#define u_int8_t	unsigned __int8
#define u_int		unsigned __int32
#define u_int32_t	unsigned __int32
using namespace std;

BITMAPFILEHEADER File_header;
BITMAPINFOHEADER Info_header;

float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
float RGBYUV01684[256], RGBYUV03316[256];
float RGBYUV04187[256], RGBYUV00813[256];
	//变量初始化
	u_int frameWidth = 256;			
	u_int frameHeight = 256;		
	bool flip = TRUE;				
	u_int size, i, k, j, t = 0;
	char* a = NULL;
	char* bmpFileName = NULL;
	char* yuvFileName = NULL;
	FILE* bmpFile = NULL;
	FILE* yuvFile = NULL;
	u_int8_t* rgbBuf = NULL;
	u_int8_t* yBuf = NULL;
	u_int8_t* uBuf = NULL;
	u_int8_t* vBuf = NULL;
	u_int32_t videoFramesWritten = 0;
	//命令行参数
	k = atoi(argv[6]);//图像重复帧数
	yuvFileName = argv[7];
  1. 调色板判断
bool MakePalette(FILE* pFile, BITMAPFILEHEADER& file_h, BITMAPINFOHEADER& info_h, RGBQUAD* pRGB_out)
{
	/*如果图像开始位置与信息头结束的位置中间,还有2的info_h.biBitCount次方(颜色数)个RGBUAQ空间,则存在调色板*/
	if ((file_h.bfOffBits - sizeof(BITMAPFILEHEADER) - info_h.biSize) == sizeof(RGBQUAD) * pow(2, (float)info_h.biBitCount))
	{
		fseek(pFile, sizeof(BITMAPFILEHEADER) + info_h.biSize, 0);
		fread(pRGB_out, sizeof(RGBQUAD), (unsigned int)pow(2, (float)info_h.biBitCount), pFile);
		return true;
	}
	else
		return false;
}
  1. BMP2RGB
//bmp转rgb
void ReadRGB(BITMAPFILEHEADER& file_h, BITMAPINFOHEADER& info_h, FILE* bmpFile, unsigned char* rgbDataOut)
{
	unsigned long Loop, i, j;
	unsigned char mask = 0;
	unsigned char* Data;
	unsigned long width, height;

	/*计算实际的宽高*/
	//由于需与DWORD对齐,所以每行需满足为4字节的整数倍,若不是则需补成其整数倍
	if (((info_h.biWidth * info_h.biBitCount) % 4) == 0)
		width = info_h.biWidth / 8 * info_h.biBitCount;
	else
		width = (info_h.biWidth * info_h.biBitCount + 31) / 32 * 4;
	//判断高是否为偶数,若不是,补齐
	if ((info_h.biHeight % 2) == 0)
		height = info_h.biHeight;
	else
		height = info_h.biHeight + 1;

	Data = (unsigned char*)malloc(height * width);
	//写入数据到Data中
	fseek(bmpFile, file_h.bfOffBits, 0);
	if (fread(Data, height * width, 1, bmpFile) != 1)
	{
		printf("read file error!");
		exit(0);
	}

	RGBQUAD* pRGB = (RGBQUAD*)malloc(sizeof(RGBQUAD) * (unsigned int)pow(2, (float)info_h.biBitCount));//把unsigned char 改为unsigned int,否则溢出

	if (!MakePalette(bmpFile, file_h, info_h, pRGB))
		printf("No palette!");

	//深度为24bit时,不需要调用调色板
	if (info_h.biBitCount == 24)
	{
		memcpy(rgbDataOut, Data, height * width);//将Data中的数据直接拷到rgbDataOut
		free(Data);
		return;
	}
	free(Data);
	free(pRGB);
}
  1. YUV循环写入
		//write yuv file
		for (i = 0; i < k; i++)
		{
			fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
			fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
			fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
			printf("\r...%d", ++videoFramesWritten);

		}
  1. 主程序调用
	yuvFile = fopen(yuvFileName, "wb");
	if (yuvFile == NULL)
	{
		printf("cannot find yuv file\n");
		exit(1);
	}
	else
	{
		printf("The output yuv file is %s\n", yuvFileName);
	}

	for (j = 1; j < 6; j++)
	{
		bmpFileName = argv[j];
		/* open the bmp file */

		bmpFile = fopen(bmpFileName, "rb");
		if (bmpFile == NULL)
		{
			printf("cannot find bmp file\n");
			exit(1);
		}
		else
		{
			printf("The input bmp file is %s\n", bmpFileName);
		}
		//读取位图文件头
		if (fread(&File_header, sizeof(BITMAPFILEHEADER), 1, bmpFile) != 1)
		{
			printf("read file header error!");
			exit(0);
		}
		if (File_header.bfType != 0x4D42)
		{
			printf("Not bmp file!");
			exit(0);
		}
		else
		{
			a = (char*)&File_header.bfType;
			printf("this is a %s\n", a);
			//printf("this is a %x\n",file_h.bfType);
		}
		//读取信息头
		if (fread(&Info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1)
		{
			printf("read info header error!\n\n");
			return false;
		}
		//	end read header
		frameWidth = Info_header.biWidth;
		frameHeight = Info_header.biHeight;
		size = frameWidth * frameHeight;
		//开辟缓冲区
		rgbBuf = (u_int8_t*)malloc(size * 3);
		yBuf = (u_int8_t*)malloc(size);
		uBuf = (u_int8_t*)malloc(size / 4);
		vBuf = (u_int8_t*)malloc(size / 4);
		//从BMP文件中读取RGB
		ReadRGB(File_header, Info_header, bmpFile, rgbBuf);
		//RGB转YUV
		if (RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip))
		{
			printf("error");
			return 0;
		}

四、总结反思

结果示例

yuv录屏


整体效果还可以,为了限制大小选择了每张图片12帧的形式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月婵婵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值