视频编码课程小作业

该博客介绍了使用C语言进行视频编码的过程,具体涉及I帧和P帧的编码。通过初始化参数、DCT变换、量化、反量化等步骤,实现了对两帧视频的编码,并计算了PSNR。编码后的视频帧与原视频宏块对比差异小,主观质量上细节略有损失,可通过调整QP值优化编码质量。
摘要由CSDN通过智能技术生成

一、视频编码题目

给定视频序列“foremam.qcif”,利用C语言完成视频编码。
这里我们只编第一帧和第二帧。

二、 程序设计步骤

1、初始化参数设置。初始化编码2帧图像需要的参数,比如视频分辨率,图像宏块划分,DCT变化矩阵,量化参数QP、Qstep等设置。

2、读取视频序列帧。读取原始视频序列“foremam_qcif.yuv”的第一帧和第二帧,设置第一帧为I帧,第二帧为P帧,存入分配好内存的数组,数组指针指向第一个像素。

2、I帧编码。对I帧的所有像素减去128得到残差,即用128的像素值对I帧进行预测,可以将0-255的像素值平移到-128到127,方便后续进行变换和量化操作。将I帧所有像素划分为4×4宏块,循环对每一个宏块依次进行DCT变换、量化,然后进行反量化和反变换,将结果写入“re_foremam_qcif.yuv”。

3、P帧编码。将P帧减去第二步重建I帧得到残差像素,即P帧预测结果。将残差图像划分为4×4宏块,循环对每一个宏块依次进行DCT变换、量化,然后进行反量化和反变换,将结果接着写入“re_foremam_qcif.yuv”。

4、编码结束,释放内存和指针。计算PSNR。

三、代码

//author-JackTuo

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<malloc.h>
#include<math.h>

//矩阵求积函数
void  matrixMultiply(int a[4], int b[4], int c[4])
{
	int i, j;
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			c[i*4+j] = a[i*4] * b[j] + a[i * 4 +1] * b[4+j] + a[i * 4 +2] * b[8 + j] + a[i * 4 +3] * b[12 + j];
		}
	}
}

//量化函数
void quant_4x4(int dct[16], int mf[16], int bias)
{
	for (int i = 0; i < 16; i++)
	{
		if (dct[i] < 0)
			dct[i] = -((abs(dct[i]) * mf[i] + bias) >> 20);
		else
			dct[i] = (abs(dct[i]) * mf[i] + bias) >> 20;
	}
}

//反量化函数
void dequant_4x4(int dct[16], int dequant_mf[16], int i_qp)
{
	int t = i_qp / 6;
	int i_mf = (int)(pow(2 ,t));
	//const int i_qbits = i_qp / 6 - 4;
	for (int i = 0; i < 16; i++)
		dct[i] = dct[i] * dequant_mf[i] * i_mf;

}

//PSNR计算
int PSNR(int mse)
{
	int psnr;
	psnr = 10 * log10(pow(255 ,2)/ mse);
	return psnr;
}

void main()
{
	//初始化参数设置
	int width = 176, height = 144;
	int mbw = width / 4, mbh = height*3 / 8;

	int mf[16];
	mf[0] = mf[2] = mf[8] = mf[10] = 11916;
	mf[5] = mf[7] = mf[13] = mf[15] = 4660;
	mf[1] = mf[3] = mf[4] = mf[6] = mf[9] = mf[11] = mf[12] = mf[14] = 7490;

	int qp = 31, qstep = 22;
	int qbits = 15 + qp / 6;
	int f1 = pow(2, qbits) / 3;
	int f2 = pow(2, qbits) / 6; 

	int dequantmf[16];
	dequantmf[0] = dequantmf[2] = dequantmf[8] = dequantmf[10] = 11;
	dequantmf[5] = dequantmf[7] = dequantmf[13] = dequantmf[15] = 18;
	dequantmf[1] = dequantmf[3] = dequantmf[4] = dequantmf[6] = dequantmf[9] = dequantmf[11] = dequantmf[12] = dequantmf[14] = 14;

	//DCT matrix
	int cf[16], ci[16], cfc[16], cic[16];
	cf[0] = cf[1] = cf[2] = cf[3] = cf[5] = cf[8] = cf[11] = cf[12] = 1;
	cf[6] = cf[9] = cf[10] = cf[15] = -1;
	cf[4] = cf[14] = 2;
	cf[7] = cf[13] = -2;

	cfc[0] = cfc[2] = cfc[3] = cfc[4] = cfc[5] = cfc[8] = cfc[12] = cfc[14] = 1;
	cfc[6] = cfc[9] = cfc[10] = cfc[15] = -1;
	cfc[1] = cfc[11] = 2;
	cfc[7] = cfc[13] = -2;

	ci[0] = ci[1] = ci[2] = ci[4] = ci[8] = ci[11] = ci[12] = ci[14] = 2;
	ci[6] = ci[7] = ci[10] = ci[13] = -2;
	ci[3] = ci[5] = 1;
	ci[9] = ci[15] = -1;

	cic[0] = cic[1] = cic[2] = cic[3] = cic[4] = cic[8] = cic[11] = cic[14] = 2;
	cic[7] = cic[9] = cic[10] = cic[13] = -2;
	cic[5] = cic[12] = 1;
	cic[6] = cic[15] = -1;

	//读取视频序列第1、2帧
	FILE* fp = fopen("E:\\foreman_qcif.yuv", "rb");

	if (fp == NULL)
	{
		printf("打开视频文件失败! \n");
		return -1;
	}

	unsigned char* frame = (unsigned char*)malloc(width * height * 3);
	unsigned char* reframe = (unsigned char*)malloc(width * height * 3);

	fread(frame, 1, width * height * 3, fp);   //将读取的视频帧存入数组

	//code第1帧
	for (int i = 0; i < mbh; i++)
	{
		for (int j = 0; j < mbw; j++)
		{
			int dct[16],d[16],d1[16];
			//宏块残差传递
			for (int y = 0; y < 4; y++)
			{
				for (int x = 0; x < 4; x++)
				{
					d[x + y * 4] = frame[i * mbw * 16 + j * 4 + x + y * 176]-128;
				}
			}

			//DCT变换
			matrixMultiply(cf, d, d1);
			matrixMultiply(d1, cfc, dct);

			//量化
			quant_4x4(dct, mf, f1);  

			//反量化
			dequant_4x4(dct, dequantmf,qp);

			//反变换
			matrixMultiply(ci,dct,d1);
			matrixMultiply(d1,cic,dct);
			for (int y = 0; y < 4; y++)
			{
				for (int x = 0; x < 4; x++)
				{
					reframe[i * mbw * 16 + j * 4 + x + y * 176] = dct[x + y * 4]/256 +128;
				}
			}
		}
	}

	//code第2帧
	for (int i = 0; i < mbh; i++)
	{
		for (int j = 0; j < mbw; j++)
		{
			int dct[16], d[16], d1[16];
			//宏块残差传递
			for (int y = 0; y < 4; y++)
			{
				for (int x = 0; x < 4; x++)
				{
					d[x + y * 4] = frame[width*height*3/2 + i * mbw * 16 + j * 4 + x + y * 176]- reframe[i * mbw * 16 + j * 4 + x + y * 176];
				}
			}

			//变换
			matrixMultiply(cf, d, d1);
			matrixMultiply(d1, cfc, dct);

			//量化
			quant_4x4(dct, mf, f2);

			//反量化
			dequant_4x4(dct, dequantmf, qp);

			//反变换
			matrixMultiply(ci, dct, d1);
			matrixMultiply(d1, cic, dct);
			for (int y = 0; y < 4; y++)
			{
				for (int x = 0; x < 4; x++)
				{
					reframe[width*height*3/2 +i*mbw*16 + j * 4 + x + y * 176] = dct[x + y * 4] / 256 + reframe[i * mbw * 16 + j * 4 + x + y * 176];
				}
			}
		}
	}

    //Writefile
	FILE* fp1 = fopen("E:\\re_foreman_qcif.yuv", "wb+");  //保存编码后的视频帧
	fwrite(reframe, 1, width * height * 3, fp1);

	//计算PSNR
	int ssd = 0;
	int mse , psnr;
	for (int i = 0; i < width * height*3; i++)
	{
		ssd += (reframe[i] - frame[i]) * (reframe[i] - frame[i]);
	}
	mse =(ssd / (width * height*3));
	psnr = PSNR(mse);
	printf("视频编码结束   \n PSNR=%d  \n", psnr);

	//程序结束关闭文件,释放内存
	fclose(fp);
	fclose(fp1);
	free(frame);
	free(reframe);
}

四、结果分析

1、宏块对比分析
在这里插入图片描述
如上图所示,其中“foremam_qcif.yuv”为原视频文件,“re_foremam_qcif.yuv”为编码后再解码的视频文件。随机抽选第一帧和第二帧的宏块,对比编码前后视频帧宏块的数据差异,可知编码前后视频帧宏块像素大致一样,误差较小。

2、主观质量分析
在这里插入图片描述

在这里插入图片描述

如上图所示,使用YUVPlayer播放器来播放测试的YUV视频文件。其中“foremam_qcif.yuv”为原视频文件,“re_foremam_qcif.yuv”为编码后再解码的视频文件。通过人眼主观对比,视频帧差距不大,但细节不够清晰。可以通过设置QP值来提高编码质量,丰富细节。QP值越小,细节越清晰,但码率会变大。需要通过率失真优化和码率控制来权衡整体编码参数选择。

ps:视频编码交流学习,多多指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值