Lab2 bmp文件与yuv文件的转换——C语言代码实现

Lab2 bmp文件与yuv文件的转换——C语言代码实现

一、实验内容

  1. 自行生成多个BMP文件,至少含5个不同的场景画面,要求带含有班级、学号后四位和本人姓名(缩写或昵称均可)的logo。
  2. 编写代码实现将第一步所生成的多个BMP文件转化为YUV文件,要求可在命令行中设置每个画面出现的帧数,且最后形成的YUV文件应至少包含200帧。
  3. 对整个程序进行调试,并将生成的YUV文件用播放软件观看,验证是否正确。

已知:

  • 自行生成了6个bmp文件
  • 每个bmp文件均为256*256
  • 每个bmp文件均为32bit深度
  • 每个bmp文件在输出的yuv文件中均出现40帧
  • 输出的yuv文件为4:2:0的采样格式

具体的素材图片如下:

素材源于网络

素材

二、实验基础

bmp图像文件的数据组成

图1
图2
图3
图4
图片源自数据压缩第二章课件

除此之外,有关于bmp文件还有几点需要特别注意:

  • bmp图像规定每一扫描行的字节数必须是4的整数倍,即以DWORD对齐。(如不满4的整数倍,则采取补0处理)
  • 对于倒向DIB,扫描行是由底向上存储的

三、实现思路

1、定义相关变量,对于输出的lab2_out.yuv文件设置相应的指针。
2、设置for循环,用以循环读取输入的lab2_x的bmp文件数据。
3、对于每一个输入的bmp文件,先读取其File_header和Info_header,在读取实际的位图文件图像数据。
4、调用Lab1中RGB2YUV转换函数,进行转换。
5、将转换好的yuv数据写入输出的lab2_out.yuv文件,并循环40次。
6、释放缓冲、关闭文件。

!!需要注意的小问题!!

  • 由于输出的lab2_out.yuv文件只有一个,且需要追加写入数据,所以使用lab2_out.yuv文件的yuvFile指针打开文件、关闭lab2_out.yuv文件的操作都需要在读取输入的lab2_x的bmp文件数据的循环外执行,但对于每个bmp文件的打开、数据的读取和相关内存的释放则需要在该循环中实现
  • 对于程序中需要输入的数据,可以使用命令参数写入程序中,既简单又高效。
  • 在读取File_header和Info_header的数据时,要使用结构体变量,既便捷又能保证高准确率,便于后续调用结构体变量中的图像宽biWidth、高biHeight。(注意添加相关的头文件
  • 由于本次实验所使用的bmp文件均为32bit深度,是真彩图像,但其数据部分为各像素的B、G、R、A数据的循环,所以在RGB2YUV的转换函数中,需要调整b指针的"b+=3"为"b+=4"

四、实现代码

bmp2yuv.h

int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip);

void InitLookupTable();

main.cpp

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include<windows.h>
#include"bmp2yuv.h"

BITMAPFILEHEADER File_header;  /* 结构体变量 */
BITMAPINFOHEADER Info_header;

int main(int argc, char** argv)
{
	char* bmpFileName = NULL;
	char* yuvFileName = NULL;
	FILE* bmpFile = NULL;
	FILE* yuvFile = NULL;

	unsigned char* bmpBuf = NULL;
	unsigned char* yBuf = NULL;
	unsigned char* uBuf = NULL;
	unsigned char* vBuf = NULL;

	long frameWidth;
	long frameHeight;
	bool flip = true;
	
	/* open the YUV file */
	yuvFileName = argv[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);
	}

	int bmpNum;
	bmpNum = atoi(argv[2]);
	for(int j=3;j<=bmpNum+2;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);
		}
		
		/*read file&info header*/
		if(fread(&File_header,sizeof(BITMAPFILEHEADER),1,bmpFile)!=1)
		{
			printf("read file header error!\n");
			exit(0);
		}
		if(File_header.bfType!=0x4D42)
		{
			printf("Not bmp file!\n");
			exit(0);
		}
		else
		{
			printf("This is a bmp file!\n");
		}
		if(fread(&Info_header,sizeof(BITMAPINFOHEADER),1,bmpFile)!=1)
		{
			printf("read info header error!\n");
			exit(0);
		}

		frameWidth = Info_header.biWidth;
		frameHeight = Info_header.biHeight;

		/* get an input buffer for a frame */
		bmpBuf = (unsigned char*)malloc(frameWidth * frameHeight * 4);

		/* get the output buffers for a frame */
		yBuf = (unsigned char*)malloc(frameWidth * frameHeight);
		uBuf = (unsigned char*)malloc((frameWidth * frameHeight) / 4);
		vBuf = (unsigned char*)malloc((frameWidth * frameHeight) / 4);

		if (bmpBuf == NULL || yBuf == NULL || uBuf == NULL || vBuf == NULL)
		{
			printf("no enought memory\n");
			exit(1);
		}

		if(fread(bmpBuf, 1, frameWidth * frameHeight * 4, bmpFile)==NULL)
		{
			printf("read data error!\n");
			exit(1);
		}

		/*change bmp to yuv*/
		if(RGB2YUV (frameWidth, frameHeight, bmpBuf, yBuf, uBuf, vBuf, flip))
		{
			printf("error");
			exit(0);
		}

		for (int i = 0; i < frameWidth*frameHeight; i++)
		{
			if (yBuf[i] < 16) yBuf[i] = 16;
			if (yBuf[i] > 235) yBuf[i] = 235;
		}

		for (int i = 0; i < frameWidth*frameHeight/4; i++)
		{
			if (uBuf[i] < 16) uBuf[i] = 16;
			if (uBuf[i] > 240) uBuf[i] = 240;

			if (vBuf[i] < 16) vBuf[i] = 16;
			if (vBuf[i] > 240) vBuf[i] = 240;
		}

		/*write data into yuv*/
		for (int i = 0; i < 40; i++)
		{
			fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
			fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
			fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
		}

		free(bmpBuf);
		free(yBuf);
		free(uBuf);
		free(vBuf);

		fclose(bmpFile);	
	}

	fclose(yuvFile);

	return(0);
}

rgb2yuv.cpp的具体实现代码已于前文链接给出,此处不再赘述。

五、结果验证

结果1
使用YUVviewerPlus播放输出的lab2_out.yuv文件(部分结果截图如下):
结果2
结果3
从输出的命令行窗口和lab2_out.yuv的播放结果来看,已经成功将6幅BMP文件转为了YUV文件,且每个BMP文件重复了40帧,结果正确。

六、总结

  • 在本次实验中,使用了命令参数和结构体变量,极大减轻了代码的书写量,简单高效。
  • 对于个别不确定用法的函数,应及时查看有关定义,而不是在报错后再去排查问题。
  • 对于自己开辟的内存,在使用完毕后要及时释放,以免出现内存不可用的问题。

总的来说,这次的实验完成地比较顺利,希望下次继续努力吖!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值