【实验二】C语言实现bmp图片序列生成yuv视频

一、实验要求

1、解析BMP格式文件,获取图像信息
2、转化BMP图像为YUV格式的图像
3、多张BMP图像,转化为YUV视频

二、实验内容

1.获取图片

获取(540*720)的bmp图片若干:
在这里插入图片描述

2.bmp格式基础

实验使用的是24位真彩色bmp文件。文件分为三个部分:

  • FileHeader
  • InfoHeader
  • 图片RGB信息

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

3.编程实现

程序由【实验一】中rgb转yuv的代码修改而来。

主函数

主函数实现以下过程:

  • 读参数中的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()函数就是【实验一】中老师给的代码,所以不贴出。

调试参数

将需要生成的yuv文件名和bmp文件名作为参数进行输入:
在这里插入图片描述

转场函数 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;
}

划像

u_int8_t* getInsertFrames_sweap(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 h = 0; h < frameHeight; h++) {
    
    
			for (int w = 0; w < frameWidth; w++) {
    
    
				if (currentFrame * frameHeight / frame > h) {
    
    
					*(mix + h * frameWidth + w) = *(buf2 + h * frameWidth + w);
				}
				else {
    
    
					*(mix + h * frameWidth + w) = *(buf1 + h * frameWidth + w);
				}
			}
		}
	return mix;
}

三、实验结果

实验结果使用LICEcap软件录制GIF图片,csdn只能支持不超过5M图片,因此只录了一次图片切换。

(1)叠加

(2)划像

四、实验总结:

  • C语言的结构体很方便快速读取文件的各部分信息
  • 对于yuv视频,没有任何压缩非常占空间,冗余度非常高

猜你喜欢

转载自blog.csdn.net/hdkks/article/details/115217174
今日推荐