H264-整数DCT变换和蝶形变换代码实现

背景

为压缩数据,H264标准需要使用整数DCT变换把图像信号转换到频域。变换过程实际上就是矩阵乘法,使用蝶形变换可以减少运算量,提升算法性能。

原理

整数DCT变换公式推导过程不再赘述,可以参考《深入理解视频编解码技术 --基于H264标准及参考模型》。
以4x4为例,变换公式如下:

  Y = Cf * X * Cf.T
  其中 Cf = 
  	[1, 1,  1,  1,
     2, 1, -1, -2,
     1,-1, -1,  1,
     1,-2,  2, -1]

展开如下:在这里插入图片描述

  • 先以Cf*X 为例,矩阵乘需要进行64次乘法和48次加法,这将耗费较多的时间。蝶形算法则是利用构造的矩阵的整数性质和对称性,将乘法运算转化为加法运算。
    先分析两个相乘矩阵的第一列,结果如下:
    p0 = x 00 + x 30 \color{#FF0000} { x00 + x30} x00+x30 + x 10 + x 20 \color{#00FF00}{x10 + x20 } x10+x20
    p1 = 2 ∗ x 00 + 2 ∗ x 30 \color{#FF0000}{2 * x00+2 * x30} 2x00+2x30 + x 10 − x 20 \color{#00FF00}{x10 - x20} x10x20
    p2 = x 00 − x 30 \color{#FF0000} { x00 - x30} x00x30 - x 10 − x 20 \color{#00FF00}{x10 - x20} x10x20
    p3 = x 00 − x 30 \color{#FF0000} {x00- x30} x00x30 - 2 ∗ x 10 + 2 ∗ x 20 \color{#00FF00}{2 * x10 + 2 * x20} 2x10+2x20
  • 从上面公式我们发现有重复运算(使用不同颜色标识),从一般算法意义上来说,可以用空间代价换时间代价,比如设置中间变量来减少计算次数。
    提取中间结果:
    M0 = x00 + x30
    M3 = x00 - x30
    M1 = x10 + x20
    M2 = x10 - x20
  • 存储了这四个中间变量,我们对比看看蝶形图,和图中第一层的算式相符合。用这些中间变量来组合,就可以把最终的p0…p3, 计算出来。这样,就把运算量降低到2个乘法和8个加法,剩余的运算就是叠代这个算法。
    在这里插入图片描述

所以,可以得出以下结论:

  • 这个蝶形图和一般意义的FFT或FDCT蝶形图不同,是对H.264在整数DCT基础上的具体算法优化,只对于以上Cf矩阵。
  • 计算过程是把上面的三个4x4矩阵乘法分成两两矩阵相乘。再把残差矩阵和后来的中间结果Cf x X一行行分别输入蝶形图进行一维整数DCT计算。
  • 蝶形图优化思想就是提取矩阵的相关部分,定义中间变量,减少运算次数。
代码示例

以下代码分别用传统矩阵运算和蝶形运算计算结果并对比结果

#include <iostream>
using namespace std;

//Cf矩阵
int Cf[4][4]=
{
    1, 1,  1,  1,
    2, 1, -1, -2,
    1,-1, -1,  1,
    1,-2,  2, -1
};

void matrixTransform(int a[][4])
{
	int i, j;
	int tmp;
	for(i=0; i<4; i++)
	{
		for(j=i; j<4; j++)
		{
			tmp = a[i][j];
			a[i][j] = a[j][i];
			a[j][i] = tmp;
		}
	}
}

//矩阵求积
// c = a * b
void  matrixMultiply_4x4(int a[][4],int b[][4],int c[][4])
{
	int i, j, k;
	for(i = 0; i < 4; i++)
	{
		for(j = 0; j < 4; j++)
		{
			c[i][j] = 0;
			for(k = 0; k < 4; k++)
			{
				c[i][j] += a[i][k] * b[k][j];  
			}
		}
	}      
}

//矩阵显示
void matrixShow(int a[][4])
{
    int i, j;
    cout << "*****************************" << endl;
    for(i = 0; i < 4; i++)
	{
        for(j = 0; j < 4; j++)
        {
            cout << a[i][j] << "\t";
        }
		cout << endl;
	}
 
    cout << "*****************************" << endl << endl;
}

// 蝶形变换求解 Cf* orgYUV * Cf.T
// b为输出matrix, a为输入 
void forward4x4(int a[][4], int b[][4])
{
	int tmp[16];
	int p0,p1,p2,p3;
	int t0,t1,t2,t3;
	int i, j;
	int *ptmp = tmp;
	
	//horizontal, Cf*orgYUV 
	for(i=0; i<4; i++)
	{
		t0 = a[0][i];
		t1 = a[1][i];
		t2 = a[2][i];
		t3 = a[3][i];
		
		p0 = t0+t3;
		p1 = t1+t2;
		p2 = t1-t2;
		p3 = t0-t3;
		
		*(ptmp++) = p0+p1;
		*(ptmp++) = p2+ (p3<<1); // 加运算符优先级要于左移 
		*(ptmp++) = p0-p1;
		*(ptmp++) = p3- (p2<<1);
	}	
	
	//vertical
	for(int i=0; i<4; i++)
	{
		t0 = tmp[i];
		t1 = tmp[i+4];
		t2 = tmp[i+2*4];
		t3 = tmp[i+3*4];
		
		p0 = t0+t3;
		p1 = t1+t2;
		p2 = t1-t2;
		p3 = t0-t3;
		
		b[i][0] = p0+p1;
		b[i][1] = p2+ (p3<<1);
		b[i][2] = p0-p1;
		b[i][3] = p3- (p2<<1);
	}
}

int main(int argc, char** argv) {
	//原始的YUV矩阵
	int orgYUV[4][4] =
	{
		-85, 88, 126, 121,
	    -79, 70,  65,  83,
        -80, 66,  49,  43,
        -82, 86,  97,  41
	};
	
	int D[4][4];
	int W[4][4];
	// W = Cf* orgYUV * Cf.T
	matrixMultiply_4x4(Cf, orgYUV, D);
	matrixTransform(Cf);	
	matrixMultiply_4x4(D, Cf, W);

	cout << "====== W =====" << endl;
	matrixShow(W);
	
	forward4x4(orgYUV, W);
	cout << "====== butter W =====" << endl;
	matrixShow(W);
	
	return 0;
}
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值