【实验二】BMP图像转成YUV视频并加转场

一、实验目的

1.理解图像文件的基本组成。

2.掌握结构体作为复杂数据对象的用法,进一步熟练由问题到程序 的解决方法,并掌握编程细节:如内存分配,倒序读写,字节序,文件读写过程等等。

3.学会将BMP图像转换为YUV图像进一步转换为YUV视频,自学转场代码的编写。

二、实验过程

1.获取图片

获取bmp格式图片三张:

2.bmp格式基础

 BMP文件采用位映射存储格式,大多只通过选择位的大小进行压缩,因此会占用比较大的空间,BMP文件的图像深度(每个像素用几bit表示)可选1bit、4bit、8bit以及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。

典型的BMP图像文件由四部分组成:

  • 位图文件头FileHeader
  • 位图信息头InfoHeader
  • 调色板Palette
  • 实际的位图数据ImageData

使用#include<windows.h>可以方便地将文件头信息读入结构体

3.编程实现

主函数

主函数实现以下过程:

读参数中的bmp文件,获取宽高,色彩信息
rgb转yuv
从第二张图片开始写入转场
写静态图片
将当前图片存入temp中,留给下一张转场用
代码:

int main(int argc, char** argv)
{
	u_int8_t* y_temp;
	u_int8_t* u_temp;
	u_int8_t* v_temp;
	int transFrames = 30;//转场帧数
	int photoFrames = 30;//静止图片帧数
	for (int pic = 2; pic < argc; pic++) //从第二个参数到最后一个是图片
	{

		//1.读bmp文件头,获取宽高
		BITMAPFILEHEADER File_header;
		BITMAPINFOHEADER Info_header;
		FILE* bmpFile;
		bmpFile = fopen(argv[pic], "rb");
		fread(&File_header, sizeof(BITMAPFILEHEADER), 1, bmpFile);
		fread(&Info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile);
		u_int frameWidth;			
		u_int frameHeight;		   
		frameWidth = Info_header.biWidth;
		frameHeight = Info_header.biHeight;

		//2.色彩信息读入rgb_buffer
		u_int8_t* rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);
		fread(rgbBuf, 1, frameWidth * frameHeight * 3, bmpFile);
		fclose(bmpFile);
		
		//3.追加模式打开yuv文件
		FILE* yuvFile = fopen(argv[1], "ab+");

		//4.rgb转yuv
		u_int8_t*  yBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
		u_int8_t*  uBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);
		u_int8_t*  vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);

		if (RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, FALSE))
		{
			printf("error");
			return 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;
		}
		
		//5.从第二张图片开始加入转场
		if (pic > 2) {
			for (int frame = 0; frame < transFrames; frame++) {
				u_int8_t* y_mix = getInsertFrames(y_temp, yBuf, transFrames, frame, frameWidth, frameHeight);
				u_int8_t* u_mix = getInsertFrames(u_temp, uBuf, transFrames, frame, frameWidth / 2, frameHeight / 2);
				u_int8_t* v_mix = getInsertFrames(v_temp, vBuf, transFrames, frame, frameWidth / 2, frameHeight / 2);
				fwrite(y_mix, 1, frameWidth * frameHeight, yuvFile);
				fwrite(u_mix, 1, (frameWidth * frameHeight) / 4, yuvFile);
				fwrite(v_mix, 1, (frameWidth * frameHeight) / 4, yuvFile);
			}
		}
		
		//6.写静态图片
		for (int frame = 0; frame < photoFrames; frame++) {
			fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
			fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
			fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
		}
			
		printf("\n%u %ux%u video frames written\n",
			pic, frameWidth, frameHeight);
		fclose(yuvFile);

		
		//7.将当前图片存入temp中,留给下一张转场用
		y_temp = (u_int8_t*)malloc(frameWidth * frameHeight);
		u_temp = (u_int8_t*)malloc(frameWidth * frameHeight*0.5);
		v_temp = (u_int8_t*)malloc(frameWidth * frameHeight*0.5);
		for (int i = 0; i < frameWidth * frameHeight; i++)
		{
			*(y_temp + i) = *(yBuf + i);
		}
		for (int i = 0; i < frameWidth * frameHeight/4; i++)
		{
			*(u_temp + i) = *(uBuf + i);
			*(v_temp + i) = *(vBuf + i);
		}

	}
	return(0);
}

其中,RGB2YUV()函数就是【实验一】中老师给的代码,所以不贴出
转场函数 getInsertFrames()

getInsertFrames()函数用来获得两张图片的混合帧,写了叠加的转场方式

叠加:

u_int8_t* getInsertFrames_mix(u_int8_t* buf1, u_int8_t* buf2, int frame,int currentFrame, u_int frameWidth,u_int frameHeight) {
	//获取两帧混合(插值)
	//frame:总转场帧数
	//currentFrame:当前帧
	u_int8_t * mix = (u_int8_t*)malloc(frameWidth * frameHeight);
	for (int j = 0; j < frameHeight * frameWidth; j++) {
		*(mix + j) = int((*(buf2 + j)* currentFrame + *(buf1 + j)*(frame- currentFrame)) / frame);
	}
	return mix;
}


三、实验结果

四、实验总结

结构体的使用使得确定图片长、宽、偏移量等信息的获取更加简单,便于实验后续的操作。但是对于YUV视频文件,每一帧之间都相互独立,没有压缩任何空间,冗余度非常高。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值