卷积交叉分量模型(convolutional cross-component model,CCCM)基本思想和CCLM模式类似,建立亮度和色度之间模型实现从亮度重建像素预测色度像素。和CCLM一样,预测色度像素前,需要对亮度重建块进行下采样,以匹配色度块尺寸。
此外,与 CCLM 类似,可以选择使用 CCCM 的单模型或多模型变体。 多模型变体使用两个模型,一个模型用于高于平均亮度参考值的样本,另一个模型用于其余样本(和 MMLM 类似)。多模型 CCCM (Multi-model CCCM mode)模式应用于至少有 128 个参考样本可用的 PU。

1. 卷积滤波器

提出的应用 7 抽头卷积滤波器计算色度预测像素,色度预测像素的计算公式如下所示:

predChromaVal = c0C + c1N + c2S + c3E + c4W + c5P + c6B

其中,C表示当前色度样本对应位置处的亮度样本,N、S、E、W分别为当前亮度样本的相邻样本,如下图所示:

非线性项 P :

P = ( C*C + midVal ) >> bitDepth

偏置项 B:

B = midVal

偏置项 B 表示输入和输出之间的标量偏移(类似于 CCLM 中的偏移项),并设置为中间色度值(对于10 bit视频,B=512)。

2. 滤波器系数的计算

通过最小化参考区域中预测和重建色度样本之间的 MSE 来计算滤波器系数 cn。

参考区域如下图所示,由 PU 上方和左侧的 6 行/列色度样本组成。 参考区域向右延伸 1 个 PU 宽度,在 PU 边界下方延伸 1 个 PU 高度。 参考区域调整为仅包含可用样本。 蓝色区域的扩展需要支持正形空间滤波器的“side samples”,通过复制相邻重建像素(绿色区域)填充得到。

通过计算参考区域中亮度重建像素的自相关矩阵亮度重建像素与色度重建像素的之间的互相关向量来执行 MSE 最小化,如下图所示。

将自相关矩阵进行 LDL 分解,并使用反代换法计算最终的滤波器系数。 该过程大致遵循 ECM 中 ALF 滤波器系数的计算,但是选择 LDL 分解而不是 Cholesky 分解以避免使用平方根运算。 所提出的方法仅使用整数算术。

3. ECM相关代码实现

1)xCccmCreateLumaRef 函数获取参考区域和当前区域亮度重建像素的同时下采样

通过调用 xCccmCalcRefArea 函数来检查参考区域的可用像素数,并将可用的参考区域尺寸和当前区域的位置和尺寸保存下来:

  m_cccmRefArea = Area( columnsLeft, rowsAbove, refWidth, refHeight); // Position with respect to the PU
// 其中refSizeX   = m_cccmRefArea.x;                        // Reference lines available left and aboverefSizeY   = m_cccmRefArea.y;areaWidth  = m_cccmRefArea.width;                    // Reference buffer size excluding paddingsareaHeight = m_cccmRefArea.height;
void IntraPrediction::xCccmCreateLumaRef(const PredictionUnit& pu)
{const CPelBuf recoLuma = pu.cs->picture->getRecoBuf(COMPONENT_Y);const int  maxPosPicX  = pu.cs->picture->chromaSize().width  - 1;const int  maxPosPicY  = pu.cs->picture->chromaSize().height - 1;xCccmCalcRefArea(pu); // Find the reference area 寻找可用参考区域int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY;PelBuf refLuma = xCccmGetLumaRefBuf(pu, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY);int puBorderX = refSizeX + pu.blocks[COMPONENT_Cb].width;int puBorderY = refSizeY + pu.blocks[COMPONENT_Cb].height;// Generate down-sampled luma for the area covering both the PU and the top/left reference areas (+ top and left paddings)// 为覆盖PU和顶部/左侧参考区域的区域生成下采样亮度(+顶部和左侧填充)for (int y = -CCCM_FILTER_PADDING; y < areaHeight; y++){for (int x = -CCCM_FILTER_PADDING; x < areaWidth; x++){if (( x >= puBorderX && y >= refSizeY ) ||( y >= puBorderY && x >= refSizeX )){continue;}int chromaPosPicX = refPosPicX + x;int chromaPosPicY = refPosPicY + y;chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX;chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY;refLuma.at( x, y ) = xCccmGetLumaVal(pu, recoLuma, chromaPosPicX, chromaPosPicY);}}CHECK( CCCM_FILTER_PADDING != 1, "Only padding with one sample implemented" );// 填充参考区域// Pad right of top reference areafor (int y = -1; y < refSizeY; y++){refLuma.at( areaWidth, y ) = refLuma.at( areaWidth - 1, y );}// Pad right of PUfor (int y = refSizeY; y < puBorderY; y++){refLuma.at( puBorderX, y ) = refLuma.at( puBorderX - 1, y );}// Pad right of left reference areafor (int y = puBorderY; y < areaHeight; y++){refLuma.at( refSizeX, y ) = refLuma.at( refSizeX - 1, y );}// Pad below left reference areafor (int x = -1; x < refSizeX + 1; x++){refLuma.at( x, areaHeight ) = refLuma.at( x, areaHeight - 1 );}// Pad below PUfor (int x = refSizeX; x < puBorderX + 1; x++){refLuma.at( x, puBorderY ) = refLuma.at( x, puBorderY - 1 );}// Pad below right reference areafor (int x = puBorderX + 1; x < areaWidth + 1; x++){refLuma.at( x, refSizeY ) = refLuma.at( x, refSizeY - 1 );}// In dualtree we can also use luma from the right and below (if not on CTU/picture boundary)if ( CS::isDualITree( *pu.cs ) ){int ctuWidth  = pu.cs->sps->getMaxCUWidth()  >> getComponentScaleX(COMPONENT_Cb, pu.chromaFormat);int ctuHeight = pu.cs->sps->getMaxCUHeight() >> getComponentScaleY(COMPONENT_Cb, pu.chromaFormat);// Samples right of top reference areaint padPosPicX = refPosPicX + areaWidth;if ( padPosPicX <= maxPosPicX && (padPosPicX % ctuWidth) ){for (int y = -1; y < refSizeY; y++){int chromaPosPicY = refPosPicY + y;chromaPosPicY     = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY;refLuma.at( areaWidth, y ) = xCccmGetLumaVal(pu, recoLuma, padPosPicX, chromaPosPicY);}}// Samples right of PUpadPosPicX = refPosPicX + puBorderX;if ( padPosPicX <= maxPosPicX && (padPosPicX % ctuWidth) ){for (int y = refSizeY; y < puBorderY; y++){int chromaPosPicY = refPosPicY + y;chromaPosPicY     = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY;refLuma.at( puBorderX, y ) = xCccmGetLumaVal(pu, recoLuma, padPosPicX, chromaPosPicY);}}// Samples right of left reference areapadPosPicX = refPosPicX + refSizeX;if ( padPosPicX <= maxPosPicX ){for (int y = puBorderY; y < areaHeight; y++){int chromaPosPicY = refPosPicY + y;chromaPosPicY     = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY;refLuma.at( refSizeX, y ) = xCccmGetLumaVal(pu, recoLuma, padPosPicX, chromaPosPicY);}}// Samples below left reference areaint padPosPicY = refPosPicY + areaHeight;if ( padPosPicY <= maxPosPicY && (padPosPicY % ctuHeight) ){for (int x = -1; x < refSizeX + 1; x++){int chromaPosPicX = refPosPicX + x;chromaPosPicX     = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX;refLuma.at( x, areaHeight ) = xCccmGetLumaVal(pu, recoLuma, chromaPosPicX, padPosPicY);}}// Samples below PUpadPosPicY = refPosPicY + puBorderY;if ( padPosPicY <= maxPosPicY && (padPosPicY % ctuHeight) ){for (int x = refSizeX; x < puBorderX; x++) // Just go to PU border as the next sample may be out of CTU (and not needed anyways){int chromaPosPicX = refPosPicX + x;chromaPosPicX     = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX;refLuma.at( x, puBorderY ) = xCccmGetLumaVal(pu, recoLuma, chromaPosPicX, padPosPicY);}}// Samples below right reference areapadPosPicY = refPosPicY + refSizeY;if ( padPosPicY <= maxPosPicY ){// Avoid going outside of right CTU border where these samples are not yet availableint puPosPicX        = pu.blocks[COMPONENT_Cb].x;int ctuRightEdgeDist = ctuWidth - (puPosPicX % ctuWidth) + refSizeX;int lastPosX         = ctuRightEdgeDist < areaWidth ? ctuRightEdgeDist : areaWidth;for (int x = puBorderX + 1; x < lastPosX; x++) // Just go to ref area border as the next sample may be out of CTU (and not needed anyways){int chromaPosPicX = refPosPicX + x;chromaPosPicX     = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX;refLuma.at( x, refSizeY ) = xCccmGetLumaVal(pu, recoLuma, chromaPosPicX, padPosPicY);}}}
}

2)计算模型参数

xCccmCalcModels 函数计算参考区域中的亮度重建像素的自相关矩阵亮度重建像素与色度重建像素的之间的互相关向量,再使用 LDL分解求解滤波器系数。

void IntraPrediction::xCccmCalcModels(const PredictionUnit& pu, CccmModel &cccmModelCb, CccmModel &cccmModelCr, int modelId, int modelThr) const
{int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY;const CPelBuf recoCb  = pu.cs->picture->getRecoBuf(COMPONENT_Cb);const CPelBuf recoCr  = pu.cs->picture->getRecoBuf(COMPONENT_Cr);PelBuf        refLuma = xCccmGetLumaRefBuf(pu, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY);int M = CCCM_NUM_PARAMS;int sampleNum = areaWidth * areaHeight - pu.blocks[COMPONENT_Cb].width * pu.blocks[COMPONENT_Cb].height;int sampleInd = 0;// Collect reference data to input matrix A and target vector Y// 收集参考数据到输入矩阵A和目标向量Ystatic Pel A[CCCM_NUM_PARAMS][CCCM_MAX_REF_SAMPLES];static Pel YCb[CCCM_MAX_REF_SAMPLES];static Pel YCr[CCCM_MAX_REF_SAMPLES];for (int y = 0; y < areaHeight; y++){for (int x = 0; x < areaWidth; x++){if ( x >= refSizeX && y >= refSizeY ){continue;}if ( modelId == 1 && refLuma.at( x, y ) > modelThr ) // Model 1: Include only samples below or equal to the threshold{continue;}if ( modelId == 2 && refLuma.at( x, y ) <= modelThr) // Model 2: Include only samples above the threshold{continue;}// 7-tap crossA[0][sampleInd] = refLuma.at( x  , y   ); // CA[1][sampleInd] = refLuma.at( x  , y-1 ); // NA[2][sampleInd] = refLuma.at( x  , y+1 ); // SA[3][sampleInd] = refLuma.at( x-1, y   ); // WA[4][sampleInd] = refLuma.at( x+1, y   ); // EA[5][sampleInd] = cccmModelCb.nonlinear( refLuma.at( x, y) );A[6][sampleInd] = cccmModelCb.bias();YCb[sampleInd]   = recoCb.at(refPosPicX + x, refPosPicY + y);YCr[sampleInd++] = recoCr.at(refPosPicX + x, refPosPicY + y);}}if ( sampleInd == 0 ) // Number of samples can go to zero in the multimode case{cccmModelCb.clearModel(M);cccmModelCr.clearModel(M);return;}else{sampleNum = sampleInd;}// Calculate autocorrelation matrix and cross-correlation vector// 计算自相关矩阵和互相关向量static CccmCovarianceInt::TE ATA;static CccmCovarianceInt::Ty ATYCb;static CccmCovarianceInt::Ty ATYCr;memset(ATA  , 0x00, sizeof(TCccmCoeff) * CCCM_NUM_PARAMS * CCCM_NUM_PARAMS);memset(ATYCb, 0x00, sizeof(TCccmCoeff) * CCCM_NUM_PARAMS);memset(ATYCr, 0x00, sizeof(TCccmCoeff) * CCCM_NUM_PARAMS);for (int coli0 = 0; coli0 < M; coli0++){for (int coli1 = coli0; coli1 < M; coli1++){Pel *col0 = A[coli0];Pel *col1 = A[coli1];for (int rowi = 0; rowi < sampleNum; rowi++){ATA[coli0][coli1] += col0[rowi] * col1[rowi];}}}for (int coli = 0; coli < M; coli++){Pel *col = A[coli];for (int rowi = 0; rowi < sampleNum; rowi++){ATYCb[coli] += col[rowi] * YCb[rowi];ATYCr[coli] += col[rowi] * YCr[rowi];}}// Scale the matrix and vector to selected dynamic range// 将矩阵和向量缩放到选定的动态范围int matrixShift = CCCM_MATRIX_BITS - 2 * pu.cu->cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA) - ceilLog2(sampleNum);if ( matrixShift > 0 ){for (int coli0 = 0; coli0 < M; coli0++){for (int coli1 = coli0; coli1 < M; coli1++){ATA[coli0][coli1] <<= matrixShift;}}for (int coli = 0; coli < M; coli++){ATYCb[coli] <<= matrixShift;}for (int coli = 0; coli < M; coli++){ATYCr[coli] <<= matrixShift;}}else if ( matrixShift < 0 ){matrixShift = -matrixShift;for (int coli0 = 0; coli0 < M; coli0++){for (int coli1 = coli0; coli1 < M; coli1++){ATA[coli0][coli1] >>= matrixShift;}}for (int coli = 0; coli < M; coli++){ATYCb[coli] >>= matrixShift;}for (int coli = 0; coli < M; coli++){ATYCr[coli] >>= matrixShift;}}// Solve the filter coefficients using LDL decomposition// 使用LDL分解求解滤波器系数CccmCovarianceInt cccmSolver;CccmCovarianceInt::TE U;       // Upper triangular L' of ATA's LDL decompositionCccmCovarianceInt::Ty diag;    // Diagonal of Dbool decompOk = cccmSolver.ldlDecompose(ATA, U, diag, M);cccmSolver.ldlSolve(U, diag, ATYCb, cccmModelCb.params, M, decompOk);cccmSolver.ldlSolve(U, diag, ATYCr, cccmModelCr.params, M, decompOk);
}

3)计算预测像素

通过LDL求解得到的滤波器系数,实现从亮度到色度的映射。

void IntraPrediction::xCccmApplyModel(const PredictionUnit& pu, const ComponentID compId, CccmModel &cccmModel, int modelId, int modelThr, PelBuf &piPred) const
{const  ClpRng& clpRng(pu.cu->cs->slice->clpRng(compId));static Pel     samples[CCCM_NUM_PARAMS];CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu);for (int y = 0; y < refLumaBlk.height; y++){for (int x = 0; x < refLumaBlk.width; x++){if ( modelId == 1 && refLumaBlk.at( x, y ) > modelThr ) // Model 1: Include only samples below or equal to the threshold{continue;}if ( modelId == 2 && refLumaBlk.at( x, y ) <= modelThr) // Model 2: Include only samples above the threshold{continue;}// 7-tap crosssamples[0] = refLumaBlk.at( x  , y   ); // Csamples[1] = refLumaBlk.at( x  , y-1 ); // Nsamples[2] = refLumaBlk.at( x  , y+1 ); // Ssamples[3] = refLumaBlk.at( x-1, y   ); // Wsamples[4] = refLumaBlk.at( x+1, y   ); // Esamples[5] = cccmModel.nonlinear( refLumaBlk.at( x, y) );samples[6] = cccmModel.bias();piPred.at(x, y) = ClipPel<Pel>( cccmModel.convolve(samples, CCCM_NUM_PARAMS), clpRng );}}
}

ECM技术学习:卷积跨分量帧内预测模型(Convolutional cross-component intra prediction model)相关推荐

  1. ECM技术学习:解码端帧内模式推导(Decoder-side Intra Mode Derivation )

    解码端帧内模式推导(DIMD)技术是之前在VVC标准化的过程中提出的技术,因为其在解码端引入的复杂度较高,因此没有被VVC采纳.为了探索下一代压缩标准,JVET最近设立了最新的ECM参考平台,将DIM ...

  2. VVC学习之五:VTM帧内预测工具详解

    文章目录 简介 1. 扩展角度帧内模式 1.1 MPM构建和模式编码 1.2 帧内广角预测(WAIP) 1.3 基于模式的参考样本平滑(MDIS) 2. 跨分量线性模型预测(CCLM) 3. 位置自适 ...

  3. HEVC亮度分量帧内预测模式代码详解

    作者:66 (转载请注明出处) 从我自身的学习历程来看,建议大家先把理论扎实了再去理解代码,代码里也有注释,与理论的地方一对应加上一点C编程基础很容易就能理解. 我们先了解相关理论,此处可参考这一篇博 ...

  4. [HEVC] HEVC学习(五) —— 帧内预测系列之三

    [HEVC] HEVC学习(五) -- 帧内预测系列之三 今天主要介绍帧内预测一个很重要的函数initAdiPattern,它的主要功能有三个,(1)检测当前PU的相邻样点包括左上.上.右上.左.左下 ...

  5. HEVC学习(六) —— 帧内预测系列之四

    本文主要把实现亮度分量帧内预测的主函数的大体框架通过代码注释的方式介绍一下. [cpp]  view plain copy Void TEncSearch::estIntraPredQT( TComD ...

  6. VVC/VTM:帧间预测——Combined inter and intra prediction (CIIP)

    Combined inter and intra prediction (CIIP) CIIP,顾名思义,就是说对编码块进行帧间预测Pred_inter和帧内预测Pred_intra,将两个预测块加权 ...

  7. ECM技术学习:基于模板的帧内模式推导(Template based intra mode derivation )

    和基于梯度的帧内模式推导类似,ECM中还使用了基于模板的帧内模式推导. 基本原理:对于当前待预测CU,在模板区域计算预测像素和重建像素的SATD,从MPM列表中选出两个SATD最小的预测模式mode1 ...

  8. ECM技术学习:重叠块运动补偿(Overlapped Block Motion Compensation)

    重叠块运动补偿(Overlapped Block Motion Compensation,OBMC)技术是在当前块运动补偿完成之后,将使用相邻块的运动信息进行运动补偿得到的块与当前预测块进行加权,主要 ...

  9. HEVC学习(五) —— 帧内预测系列之三

    由于研究的需要,现将一晨不变大神的关于HEVC帧内预测的相关博客进行转载,方便自己查阅.一直都看一晨不变大神的帖子,受益匪浅.原著博客地址:http://blog.csdn.net/HEVC_CJL/ ...

最新文章

  1. 军规13 降低流量和电量消耗
  2. 使用 ZwUnmapViewOfSection 卸载并替换内存镜像
  3. Java引入import其它目录的自定义包或java源文件
  4. PPO-强化学习算法
  5. error: object MultivariateNormalDistribution is not a member of package
  6. DDLog-不同颜色打印信息
  7. 苹果官网买的认证翻新机可靠吗?
  8. 告别鼠标——【Windows下常见系统快捷键】
  9. python多进程间通信
  10. 设置eclipse主题风格
  11. dBm、dBW和W转换
  12. 执行npm install报错:npm ERR! code EINTEGRITY,npm ERR! 最彻底,最实用的方法就是更新node版本
  13. iOS音乐播放器实现日记
  14. 护肤品html作业,聚美优品美容产品热点.html
  15. 为什么使用 Kafka?
  16. 电力电子技术(16)——直流斩波电路
  17. BGP路径属性与选路原则
  18. 数据库大作业——学生选课系统(基于SpringBoot+Mysql)
  19. 如何在产品功能维度评价APP
  20. 软著申请材料,软著申请文件,软著登记材料,软著登记文件

热门文章

  1. 动态脑电图(Ambulatory EEG)及其工作过程、数据处理!
  2. VB中dim与redim的区别?
  3. tp6查询某个字段不等于null
  4. EXCEL中的数据分析—抽样分析
  5. LINK : fatal error LNK1104: 无法打开文件“mfc140u.lib” 错误解决方案
  6. 【压缩感知合集6】压缩感知为什么可以恢复信号;为什么需要满足稀疏性条件、RIP条件、矩阵不相关等限制条件才可以恢复信号的逻辑分析
  7. win10笔记本电脑找不到WLAN
  8. 这是什么一个可以升级的系统 可为什么没有说明书 自己摸索 你玩儿我吧 一个得到了一套可以升级的军事系统的年轻人,以后路就应该坑到底
  9. 使用pydub拼接多个MP3音频文件
  10. 独家 | 流媒体服务中的诈骗检测