变换

原理以及公式

    对于大部分图像来说,它们都有很多平坦区域和内容变换缓慢的区域,而且相邻像素点的相关性很强,通过变换,可以把这些相关性减少,同时把图像的能量在空间域的分散分布转换为在变换域的相对集中分布,这样就可以去除空间冗余了

HEVC中使用两种变换:DCT和DST。DST只处理帧内4x4模式的亮度块,其他的所有模式都使用DCT因此HEVC中,DST只有4x4的规格,而DCT有4x4、8x8、16x16、32x32等几种尺寸。

以二维的DCT为例子


可以看到二维的DCT变换可以转换成2个一维的DCT变换:
一维的形式:

但是矩阵A的元素是非整数,不利于计算,因此要做调整,把A矩阵的元素都乘上一个因子,把他们转换成整数,然后进行DCT变换,变换完成之后,在量化之前,把因子除去即可。
下面是DCT完整的形式:
其他更多细节请看 HEVC/H.265理论知识(5)——变换

蝶形变换

    蝶形变换的思想就是提取矩阵的相关部分,定义中间变量,减少运算次数。具体可以参考 H.264整数DCT公式推导及蝶形算法分析
以4x4的DCT变换为例子:
      
分析结果矩阵的第一列
定义4个临时变量
那么第一列的结果可以转换成
通过定义临时变量,减少了重复的计算,以此加快计算速度,这就是蝶形变换

代码实现

入口函数

入口函数是encodeResAndCalcRdInterCU。
这个函数的作用是根据预测值,求出残差,然后进行TU的划分,然后进行变换、量化等操作以及RD代价的计算。在前面讲解预测的时候,这个函数已经分析过了,这里不再赘述。

变换量化主函数

// 变换和量化!!!!!
Void TComTrQuant::transformNxN( TComDataCU* pcCU, Pel*        pcResidual, UInt        uiStride, TCoeff*     rpcCoeff,
#if ADAPTIVE_QP_SELECTIONInt*&       rpcArlCoeff,
#endifUInt        uiWidth, UInt        uiHeight, UInt&       uiAbsSum, TextType    eTType, UInt        uiAbsPartIdx,Bool        useTransformSkip)
{// 判断是否跳过变换量化步骤,如果跳过的话,那么变换量化系数直接就是残差系数if (pcCU->getCUTransquantBypass(uiAbsPartIdx)){uiAbsSum=0;for (UInt k = 0; k<uiHeight; k++){for (UInt j = 0; j<uiWidth; j++){rpcCoeff[k*uiWidth+j]= pcResidual[k*uiStride+j];uiAbsSum += abs(pcResidual[k*uiStride+j]);}}return;}// 亮度块的帧内预测模式UInt uiMode;  //luma intra predif(eTType == TEXT_LUMA && pcCU->getPredictionMode(uiAbsPartIdx) == MODE_INTRA ){uiMode = pcCU->getLumaIntraDir( uiAbsPartIdx );}else{uiMode = REG_DCT;}uiAbsSum = 0;assert( (pcCU->getSlice()->getSPS()->getMaxTrSize() >= uiWidth) );Int bitDepth = eTType == TEXT_LUMA ? g_bitDepthY : g_bitDepthC;// 使用变换跳过模式if(useTransformSkip){// 变换xTransformSkip(bitDepth, pcResidual, uiStride, m_plTempCoeff, uiWidth, uiHeight );}else{// 变换xT(bitDepth, uiMode, pcResidual, uiStride, m_plTempCoeff, uiWidth, uiHeight );}// 量化函数xQuant( pcCU, m_plTempCoeff, rpcCoeff,
#if ADAPTIVE_QP_SELECTIONrpcArlCoeff,
#endifuiWidth, uiHeight, uiAbsSum, eTType, uiAbsPartIdx );
}

使用了变换跳过的模式的变换操作

/*
** 使用了变换跳过模式的操作,这个操作也相当于是变换,
** 但是没有执行具体的DCT变换,只是把残差系数经过位移操作
*/
Void TComTrQuant::xTransformSkip(Int bitDepth, Pel* piBlkResi, UInt uiStride, Int* psCoeff, Int width, Int height )
{assert( width == height );UInt uiLog2TrSize = g_aucConvertToBit[ width ] + 2;Int  shift = MAX_TR_DYNAMIC_RANGE - bitDepth - uiLog2TrSize;UInt transformSkipShift;Int  j,k;if(shift >= 0){transformSkipShift = shift;for (j = 0; j < height; j++){    for(k = 0; k < width; k ++){psCoeff[j*height + k] = piBlkResi[j * uiStride + k] << transformSkipShift;      }}}else{//The case when uiBitDepth > 13Int offset;transformSkipShift = -shift;offset = (1 << (transformSkipShift - 1));for (j = 0; j < height; j++){    for(k = 0; k < width; k ++){psCoeff[j*height + k] = (piBlkResi[j * uiStride + k] + offset) >> transformSkipShift;      }}}
}

变换操作

/*
** 变换操作
*/
Void TComTrQuant::xT(Int bitDepth, UInt uiMode, Pel* piBlkResi, UInt uiStride, Int* psCoeff, Int iWidth, Int iHeight )
{
#if MATRIX_MULT  Int iSize = iWidth;// 常规的DCTxTr(bitDepth, piBlkResi,psCoeff,uiStride,(UInt)iSize,uiMode);
#elseInt j;Short block[ 32 * 32 ];// 系数Short coeff[ 32 * 32 ];// 初始化块的值for (j = 0; j < iHeight; j++){    memcpy( block + j * iWidth, piBlkResi + j * uiStride, iWidth * sizeof( Short ) );}// 使用蝶形快速变换的DCT操作// 变换之后的系数存放在coeff中xTrMxN(bitDepth, block, coeff, iWidth, iHeight, uiMode );// 将系数转存到psCoeff中for ( j = 0; j < iHeight * iWidth; j++ ){    psCoeff[ j ] = coeff[ j ];}
#endif
}

常规的DCT

/** NxN forward transform (2D) using brute force matrix multiplication (3 nested loops)
*  \param block pointer to input data (residual)
*  \param coeff pointer to output data (transform coefficients)
*  \param uiStride stride of input data
*  \param uiTrSize transform size (uiTrSize x uiTrSize)
*  \param uiMode is Intra Prediction mode used in Mode-Dependent DCT/DST only
*/
// 常规的DCT变换
void xTr(Int bitDepth, Pel *block, Int *coeff, UInt uiStride, UInt uiTrSize, UInt uiMode)
{Int i,j,k,iSum;Int tmp[32*32];const Short *iT;UInt uiLog2TrSize = g_aucConvertToBit[ uiTrSize ] + 2;// 选择变换矩阵if (uiTrSize==4){iT  = g_aiT4[0];}else if (uiTrSize==8){iT = g_aiT8[0];}else if (uiTrSize==16){iT = g_aiT16[0];}else if (uiTrSize==32){iT = g_aiT32[0];}else{assert(0);}Int shift_1st = uiLog2TrSize - 1 + bitDepth-8; // log2(N) - 1 + g_bitDepth-8Int add_1st = 1<<(shift_1st-1);Int shift_2nd = uiLog2TrSize + 6;Int add_2nd = 1<<(shift_2nd-1);/* Horizontal transform */// 水平方向的一维变换if (uiTrSize==4){if (uiMode != REG_DCT && g_aucDCTDSTMode_Hor[uiMode]){iT  =  g_as_DST_MAT_4[0];}}for (i=0; i<uiTrSize; i++){for (j=0; j<uiTrSize; j++){iSum = 0;for (k=0; k<uiTrSize; k++){iSum += iT[i*uiTrSize+k]*block[j*uiStride+k];}tmp[i*uiTrSize+j] = (iSum + add_1st)>>shift_1st;}}/* Vertical transform */// 垂直方向的一维变换if (uiTrSize==4){if (uiMode != REG_DCT && g_aucDCTDSTMode_Vert[uiMode]){iT  =  g_as_DST_MAT_4[0];}else{iT  = g_aiT4[0];}}for (i=0; i<uiTrSize; i++){                 for (j=0; j<uiTrSize; j++){iSum = 0;for (k=0; k<uiTrSize; k++){iSum += iT[i*uiTrSize+k]*tmp[j*uiTrSize+k];        }coeff[i*uiTrSize+j] = (iSum + add_2nd)>>shift_2nd; }}
}

使用蝶形变换实现的DCT

/*
** 变换操作(使用蝶形变换实现)
*/
void xTrMxN(Int bitDepth, Short *block,Short *coeff, Int iWidth, Int iHeight, UInt uiMode)
{Int shift_1st = g_aucConvertToBit[iWidth]  + 1 + bitDepth-8; // log2(iWidth) - 1 + g_bitDepth - 8Int shift_2nd = g_aucConvertToBit[iHeight]  + 8;                   // log2(iHeight) + 6Short tmp[ 64 * 64 ];// 对于4x4的块,比较特殊if( iWidth == 4 && iHeight == 4){if (uiMode != REG_DCT){// 快速变换fastForwardDst(block,tmp,shift_1st); // Forward DST BY FAST ALGORITHM, block input, tmp outputfastForwardDst(tmp,coeff,shift_2nd); // Forward DST BY FAST ALGORITHM, tmp input, coeff output}else{// 蝴蝶型变换partialButterfly4(block, tmp, shift_1st, iHeight);partialButterfly4(tmp, coeff, shift_2nd, iWidth);}}// 大小为8的时候else if( iWidth == 8 && iHeight == 8){partialButterfly8( block, tmp, shift_1st, iHeight );partialButterfly8( tmp, coeff, shift_2nd, iWidth );}// 大小为16的时候else if( iWidth == 16 && iHeight == 16){partialButterfly16( block, tmp, shift_1st, iHeight );partialButterfly16( tmp, coeff, shift_2nd, iWidth );}// 大小为32的时候else if( iWidth == 32 && iHeight == 32){partialButterfly32( block, tmp, shift_1st, iHeight );partialButterfly32( tmp, coeff, shift_2nd, iWidth );}
}

蝶形变换一例

/* 4x4的蝶形变换 */
void partialButterfly4(short *src, short *dst,int shift, int src_stride)//(Short *src,Short *dst,Int shift, Int line)
{int j;int E[2],O[2];int add = 1<<(shift-1);for (j=0; j<4; j++){    /* E and O */// 定义了四个临时变量,蝶形变换的核心就是使用空间代替时间// 这四个变量的目的就是减少运算的次数,只计算一次即可,下次使用的时候直接使用即可E[0] = src[0] + src[3];O[0] = src[0] - src[3];E[1] = src[1] + src[2];O[1] = src[1] - src[2];dst[0] = (g_aiT4[0][0]*E[0] + g_aiT4[0][1]*E[1] + add)>>shift;dst[2*4] = (g_aiT4[2][0]*E[0] + g_aiT4[2][1]*E[1] + add)>>shift;dst[4] = (g_aiT4[1][0]*O[0] + g_aiT4[1][1]*O[1] + add)>>shift;dst[3*4] = (g_aiT4[3][0]*O[0] + g_aiT4[3][1]*O[1] + add)>>shift;src += src_stride;dst++;}
}

HM编码器代码阅读(18)——变换相关推荐

  1. HM编码器代码阅读(20)——与变换量化有关的其他知识

    与变换量化有关的其他知识 变换 哈达玛变换 哈达玛变换是广义傅立叶变换的一种,它的变换矩阵Hm是一个2^m x 2^m的矩阵. 哈达玛变换及其矩阵有下面的几个特性: 1.hadamard矩阵元素都是正 ...

  2. HM编码器代码阅读(32)——帧间预测之AMVP/Merge模式(七)encodeResAndCalcRdInterCU函数:残差计算、变换量化

    encodeResAndCalcRdInterCU 原理和细节 经过运动估计.运动补偿,我们得到了MV以及参考块,那么接下来是计算残差.计算MVD,然后对系数进行变换.量化. encodeResAnd ...

  3. HM编码器代码阅读(13)——帧间预测之AMVP模式(一)总体流程

    帧间预测的原理 AMVP的原理 帧间预测的实质就是为当前的PU在参考帧中寻找一块最相似块(相似度的判断准则有SAD等方法).但是参考图像通常都比较大,我们直接去搜索的话就太费时了,应该使用某种方法在参 ...

  4. HM编码器代码阅读(30)——帧间预测之AMVP模式(五)运动估计

    运动估计 通过 点击打开链接 介绍的方法得到MVP之后,可以根据该MVP确定运动估计的搜索起点,然后进行运动估计 xMotionEstimation就是进行运动估计的入口函数     1.先进行一些初 ...

  5. HM编码器代码阅读(38)——帧内预测(五)帧内预测之正式的预测操作

    正式的预测操作 在前面的操作中,我们已经得到了模式候选列表,但是我们的目的是要得到一个最优的模式,因此我们还需要对这个列表中的模式进行遍历,对于每一个模式,进行预测操作,为了计算率失真代价还必须进行变 ...

  6. HM编码器代码阅读(16)——帧间预测之AMVP模式(四)预测MV的获取

    帧间预测的原理 AMVP的原理 帧间预测的实质就是为当前的PU在参考帧中寻找一块最相似块(相似度的判断准则有SAD等方法).但是参考图像通常都比较大,我们直接去搜索的话就太费时了,应该使用某种方法在参 ...

  7. HM编码器代码阅读(14)——帧间预测之AMVP模式(二)predInterSearch函数

    简介     predInterSearch主要的工作是ME(运动估计)和MC(运动补偿).     函数中有一个bTestNormalMC变量,它表示是否进行正常的MC过程,正常的MC过程就是进行M ...

  8. HM编码器代码阅读(31)——帧间预测之AMVP/Merge模式(六)运动补偿

    运动补偿 原理 说实话一直很难理解运动补偿中"补偿"二字的意思,在参考了 http://blog.csdn.net/hevc_cjl/article/details/8457642 ...

  9. HM编码器代码阅读(14)——帧间预測之AMVP模式(二)predInterSearch函数

    简单介绍     predInterSearch基本的工作是ME(运动预计)和MC(运动补偿).     函数中有一个bTestNormalMC变量.它表示是否进行正常的MC过程,正常的MC过程就是进 ...

最新文章

  1. 多目标跟踪 | AI产品经理需要了解的CV通识(三)
  2. 深入浅出说编译原理(一)
  3. 添加lombok插件
  4. 解决:fatal: Not a valid object name: ‘master‘问题
  5. java把一个list_java中将一个List等分成n个list的工具方法(推荐)
  6. 关于安装oracle 11G R2 for Windows X64问题
  7. 【Linux】一步一步学Linux——uname命令(72)
  8. 支付宝即时到账接口开发 - DEMO讲解
  9. 【新手宝典】一篇博文带萌新建站并了解建站体系流程和对萌新友好的便捷方式,这篇博文很有可能是你的启蒙文
  10. SSH HTTPS 公钥、秘钥、对称加密、非对称加密、 总结理解
  11. mysql磁盘io高是什么造成_FAQ | 是什么导致MySQL数据库服务器磁盘I/O高(本文章来自知数堂)...
  12. VSTA InfoPath如何实现下拉列表联动
  13. 面试阿里前端P6血和泪换来的收获
  14. 企业微信私聊安全吗?
  15. 缺陷检测End-to-end training of a two-stage neural networkfor defect detection(端到端的两步神经网络的缺陷检测)
  16. 构建基于 MCU 安全物联网系统
  17. 7939.com,7b.com.cn,9505.com,4199.com 清除工具(转)
  18. win10电脑桌面透明便签_DesktopNoteOK桌面便签小工具下载|windows10桌面透明便签插件_最火软件站...
  19. NASA降水量数据的单位kg/m2与mm的关系
  20. 还活着哈。 ..:D

热门文章

  1. C++版本计算n阶乘末尾0的个数原理讲解及代码实现
  2. 511遇见易语言大漠多线程自动切换账号循环登录任务模板
  3. 什么是FPGA fpga的核心作用
  4. 【vue2静态复古win95简历网页】模仿电脑状态栏实现
  5. mysql数据表名设置大小写不敏感(Linux Centos)
  6. 你觉得什么样的程序员才算真正的大神?我今天想说两句!
  7. 国内员工访问office365加速---高珊珊的博客
  8. contentEditable与suppressContentEditableWarning
  9. 互联网产品中为什么“细节决定成败”。——欢迎补充
  10. 北语的计算机语言学怎么样,谈谈,北京语言大学社会认可度怎么样