首页 > 其他分享 >openh264 帧内预测编码原理:WelsMdI4x4 函数

openh264 帧内预测编码原理:WelsMdI4x4 函数

时间:2024-06-14 17:29:08浏览次数:24  
标签:编码 预测 int32 WelsMdI4x4 模式 iCurMode iBestCost pMbCache openh264

介绍

  1. 功能:针对4x4像素块的帧内模式决策
  2. 原型
int32_t WelsMdI4x4 (sWelsEncCtx* pEncCtx, SWelsMD* pWelsMd, SMB* pCurMb, SMbCache* pMbCache)
  1. 参数
  • sWelsEncCtx* pEncCtx: 指向编码上下文结构的指针,包含编码过程中需要的状态信息。
  • SWelsMD* pWelsMd: 指向运动检测结构的指针,包含运动检测的相关信息和结果。
  • SMB* pCurMb: 指向当前宏块(Macroblock)的指针,
  • SMbCache* pMbCache: 指向宏块缓存的指针,存储宏块的临时数据。

函数关系图

在这里插入图片描述

原理

  1. 说明
  • 类似 I16x16,只不过预测模式更多;
  • 与 16x16 块预测不同的是,可以看到在 4x4 块预测结束之后直接调用WelsEncRecI4x4Y函数对每个 4x4 块进行编码,包括了变换、扫描、量化、重建操作过程。
  1. 原理过程
  • 初始化各类变量;
  • 针对每个 4x4 块,for 循环处理每个 4x4 块;
    • 计算当前4x4块在编码宏块pEncMb和解码宏块pDecMb中的位置。这是通过iCoordinateX和iCoordinateY(4x4块在宏块中的相对位置)以及宏块的行大小kiLineSizeEnc和kiLineSizeDec来实现的;
    • PredIntra4x4Mode函数根据当前块的扫描顺序索引kpCache48CountScan4[i]和缓存的内插值模式pMbCache->iIntraPredMode来预测当前块的模式;
    • 根据kiOffset(当前块的邻居关系)确定有多少可用的预测模式(iAvailCount),并获取这些模式的数组kpAvailMode;
    • 开始选取最佳预测模式,初始化iBestCost为最大值,初始化iBestMode为kpAvailMode[0];
    • 如果pfIntra4x4Combined3存在,且iAvailCount大于等于 6,
      • pDst 指向用于存储预测块的内存位置;
      • pfIntra4x4Combined3函数计算出当前块的最佳代价iBestCost和最佳模式iBestMode;
      • for循环从索引 3 开始,直到 iAvailCount,即当前块可用的预测模式数量;
        • 根据 iBestPredBufferNum 的当前值,更新 pDst 指向另一个缓冲区,以便为下一个模式生成预测;
        • fGetLumaI4x4Pred[iCurMode] 函数根据当前模式生成预测块;
        • 使用 pfSampleSatd函数计算预测块和原始编码块之间的绝对变换差分和(SATD)作为当前预测模式的代价iCurCost;
        • 比较iCurCost和iBestCost,更新iBestMode、iBestCost,并切换 iBestPredBufferNum 以指向当前最佳的预测块;
    • 否则,
      • for 循环遍历所有的预测模式;
        • 根据索引从可用模式中指向当前模式iCurMode;
        • 根据 iBestPredBufferNum 的当前值,更新 pDst 指向另一个缓冲区,以便为下一个模式生成预测;
        • fGetLumaI4x4Pred[iCurMode] 函数根据当前模式生成预测块;
        • 使用 pfSampleSatd 函数计算预测块和原始编码块之间的绝对变换差分和(SATD)作为当前预测模式的代价iCurCost;
        • 比较iCurCost和iBestCost,更新iBestMode、iBestCost,并切换 iBestPredBufferNum 以指向当前最佳的预测块;
    • pBestPredI4x4Blk4 被设置为指向当前最佳预测块的内存地址;
    • 将当前最佳模式的成本 iBestCost 加到总成本 iCosti4x4 上;
    • 如果到目前为止累积的成本 iCosti4x4 大于或等于之前计算的亮度成本 iBestCostLuma,则break退出循环。这是因为继续计算其他模式不太可能找到更低的成本,从而节省计算资源;
    • 更新预测模式和样本可用性缓存;
      • iFinalMode 是将 iBestMode 通过一个映射表 g_kiMapModeI4x4 转换后的模式值;
      • 根据当前预测模式 iPredMode 和最终模式 iFinalMode 的比较结果,更新 pPrevIntra4x4PredModeFlag 缓存。如果它们相等,则设置为 true,否则设置为 false,同时更新pRemIntra4x4PredModeFlag;
      • pRemIntra4x4PredModeFlag 指针递增,为下一个模式的更新做准备;
      • 将 iFinalMode 更新到 pMbCache->iIntraPredMode 中,这是当前4x4块的内插值预测模式缓存。这个缓存用于后续的编码过程;
    • 调用 WelsEncRecI4x4Y 函数对确定最佳预测模式的当前4x4块进行编码。这个函数会进行实际的变换、量化和重建操作;
  • 调用宏ST32LD32函数将 pMbCache->iIntraPredMode 数组中从索引33开始的32位值复制到 pCurMb->pIntra4x4PredMode 的起始位置;常是将计算得到的内插值预测模式复制到宏块的正式存储区域;
  • 将缓存pMbCache中特定位置赋值给当前宏块pCurMb中预测模式,由于在宏块中的特定位置需要特定的预测模式值;
  • iCosti4x4 变量增加了一个基于量化参数 iLambda 的值;
  • 返回总成本iCosti4x4。
  1. 原理图
    在这里插入图片描述

源码

int32_t WelsMdI4x4 (sWelsEncCtx* pEncCtx, SWelsMD* pWelsMd, SMB* pCurMb, SMbCache* pMbCache) {
  SWelsFuncPtrList* pFunc       = pEncCtx->pFuncList;
  SDqLayer* pCurDqLayer         = pEncCtx->pCurDqLayer;
  int32_t iLambda               = pWelsMd->iLambda;
  int32_t iBestCostLuma         = pWelsMd->iCostLuma;
  uint8_t* pEncMb               = pMbCache->SPicData.pEncMb[0];
  uint8_t* pDecMb               = pMbCache->SPicData.pCsMb[0];
  const int32_t kiLineSizeEnc   = pCurDqLayer->iEncStride[0];
  const int32_t kiLineSizeDec   = pCurDqLayer->iCsStride[0];

  uint8_t* pCurEnc, *pCurDec, *pDst;

  int32_t iPredMode, iCurMode, iBestMode, iFinalMode;
  int32_t iCurCost, iBestCost;
  int32_t iAvailCount;
  const uint8_t* kpAvailMode;
  int32_t i, j, iCoordinateX, iCoordinateY, iIdxStrideEnc, iIdxStrideDec;
  int32_t lambda[2] = {iLambda << 2, iLambda};
  bool* pPrevIntra4x4PredModeFlag       = pMbCache->pPrevIntra4x4PredModeFlag;
  int8_t* pRemIntra4x4PredModeFlag      = pMbCache->pRemIntra4x4PredModeFlag;
  const uint8_t* kpIntra4x4AvailCount   = &g_kiIntra4AvailCount[0];
  const uint8_t* kpCache48CountScan4    = &g_kuiCache48CountScan4Idx[0];
  const int8_t* kpNeighborIntraToI4x4   = g_kiNeighborIntraToI4x4[pMbCache->uiNeighborIntra];
  const int8_t* kpCoordinateIdxX        = &g_kiCoordinateIdx4x4X[0];
  const int8_t* kpCoordinateIdxY        = &g_kiCoordinateIdx4x4Y[0];
  int32_t iBestPredBufferNum            = 0;
  int32_t iCosti4x4                     = 0;

#if defined(X86_ASM)
  WelsPrefetchZero_mmx (g_kiMapModeI4x4);
  WelsPrefetchZero_mmx ((int8_t*)&pFunc->pfGetLumaI4x4Pred);
#endif//X86_ASM

  for (i = 0; i < 16; i++) {
    const int32_t kiOffset = kpNeighborIntraToI4x4[i];

    //step 1: locating current 4x4 block position in pEnc and pDecMb
    iCoordinateX = kpCoordinateIdxX[i];
    iCoordinateY = kpCoordinateIdxY[i];

    iIdxStrideEnc = (iCoordinateY * kiLineSizeEnc) + iCoordinateX;
    pCurEnc = pEncMb + iIdxStrideEnc;
    iIdxStrideDec = (iCoordinateY * kiLineSizeDec) + iCoordinateX;
    pCurDec = pDecMb + iIdxStrideDec;

    //step 2: get predicted mode from neighbor
    iPredMode = PredIntra4x4Mode (pMbCache->iIntraPredMode, kpCache48CountScan4[i]);

    //step 3: collect candidates of iPredMode
    iAvailCount = kpIntra4x4AvailCount[kiOffset];
    kpAvailMode = g_kiIntra4AvailMode[kiOffset];

    //step 4: gain the best pred mode
    iBestCost = INT_MAX;
    iBestMode = kpAvailMode[0];

    if (pFunc->sSampleDealingFuncs.pfIntra4x4Combined3 && (iAvailCount >= 6)) {
      pDst = &pMbCache->pMemPredBlk4[iBestPredBufferNum << 4];

      iBestCost = pFunc->sSampleDealingFuncs.pfIntra4x4Combined3 (pCurDec, kiLineSizeDec, pCurEnc, kiLineSizeEnc, pDst,
                  &iBestMode,
                  lambda[iPredMode == 2], lambda[iPredMode == 1], lambda[iPredMode == 0]);
      //     ST64(&pMbCache->pMemPredBlk4[iBestMode<<4], LD64(mem_pred_blk4_temp));
      //     ST64(&pMbCache->pMemPredBlk4[8+(iBestMode<<4)], LD64(mem_pred_blk4_temp+8));

      for (j = 3; j < iAvailCount; ++ j) {
        iCurMode = kpAvailMode[j];

        assert (iCurMode >= 0 && iCurMode < 14);

        pDst = &pMbCache->pMemPredBlk4[ (1 - iBestPredBufferNum) << 4];

        pFunc->pfGetLumaI4x4Pred[iCurMode] (pDst, pCurDec, kiLineSizeDec);
        iCurCost = pFunc->sSampleDealingFuncs.pfSampleSatd[BLOCK_4x4] (pDst, 4, pCurEnc, kiLineSizeEnc) +
                   lambda[iPredMode == g_kiMapModeI4x4[iCurMode]];

        if (iCurCost < iBestCost) {
          iBestMode = iCurMode;
          iBestCost = iCurCost;
          iBestPredBufferNum = 1 - iBestPredBufferNum;
        }
      }
    } else {
      for (j = 0; j < iAvailCount; ++ j) {
        iCurMode = kpAvailMode[j];

        assert (iCurMode >= 0 && iCurMode < 14);

        pDst = &pMbCache->pMemPredBlk4[ (1 - iBestPredBufferNum) << 4];

        pFunc->pfGetLumaI4x4Pred[iCurMode] (pDst, pCurDec, kiLineSizeDec);
        iCurCost = pFunc->sSampleDealingFuncs.pfSampleSatd[BLOCK_4x4] (pDst, 4, pCurEnc, kiLineSizeEnc) +
                   lambda[iPredMode == g_kiMapModeI4x4[iCurMode]];

        if (iCurCost < iBestCost) {
          iBestMode = iCurMode;
          iBestCost = iCurCost;
          iBestPredBufferNum = 1 - iBestPredBufferNum;
        }
      }
    }
    pMbCache->pBestPredI4x4Blk4 = &pMbCache->pMemPredBlk4[iBestPredBufferNum << 4];
    iCosti4x4 += iBestCost;
    if (iCosti4x4 >= iBestCostLuma) {
      break;
    }

    //step 5: update pred mode and sample avail cache
    iFinalMode = g_kiMapModeI4x4[iBestMode];
    if (iPredMode == iFinalMode) {
      *pPrevIntra4x4PredModeFlag++ = true;
    } else {
      *pPrevIntra4x4PredModeFlag++ = false;
      *pRemIntra4x4PredModeFlag  = (iFinalMode < iPredMode ? iFinalMode : (iFinalMode - 1));
    }
    pRemIntra4x4PredModeFlag++;
    // pCurMb->pIntra4x4PredMode[g_kuiMbCountScan4Idx[i]] = iFinalMode;
    pMbCache->iIntraPredMode[kpCache48CountScan4[i]] = iFinalMode;

    //step 6: encoding I_4x4
    WelsEncRecI4x4Y (pEncCtx, pCurMb, pMbCache, i);
  }
  ST32 (pCurMb->pIntra4x4PredMode, LD32 (&pMbCache->iIntraPredMode[33]));
  pCurMb->pIntra4x4PredMode[4] = pMbCache->iIntraPredMode[12];
  pCurMb->pIntra4x4PredMode[5] = pMbCache->iIntraPredMode[20];
  pCurMb->pIntra4x4PredMode[6] = pMbCache->iIntraPredMode[28];
  iCosti4x4 += (iLambda << 4) + (iLambda << 3); //4*6*lambda from JVT SATD0
  return iCosti4x4;
}

标签:编码,预测,int32,WelsMdI4x4,模式,iCurMode,iBestCost,pMbCache,openh264
From: https://blog.csdn.net/yanceyxin/article/details/139680513

相关文章

  • 机器学习归一化特征编码
    特征缩放因为对于大多数的机器学习算法和优化算法来说,将特征值缩放到相同区间可以使得获取性能更好的模型。就梯度下降算法而言,例如有两个不同的特征,第一个特征的取值范围为1——10,第二个特征的取值范围为1——10000。在梯度下降算法中,代价函数为最小平方误差函数,所以在使用......
  • 主流3D视频编码技术
    3D视频通过模拟人眼的立体视觉,使我们能够感受到深度和距离,提供了一种更加真实而富有沉浸感的视觉体验。长期以来,大量3D视频内容并没有使用专用的视频编码标准,而是使用通用的视频编码标准进行编码。主要的做法是将3D视频以SBS(sidebyside)的形式,把左右两个视点合并到一帧画面......
  • 短视频压缩与编码技术在短剧APP小程序开发中的应用:重要性分析
    在短剧APP小程序开发中,短视频的压缩与编码技术扮演着至关重要的角色。随着移动互联网的快速发展,用户对短视频内容的加载速度和播放质量提出了更高要求。本文将分析短视频压缩与编码技术对于短剧APP的重要性,并探讨其在实际开发中的应用。短视频压缩与编码技术的重要性提高加......
  • 短视频压缩与编码技术在短剧APP小程序开发中的应用:技术选择与工具推荐
    在短剧APP小程序开发中,选择合适的短视频压缩与编码技术及工具对于实现高效的视频处理至关重要。本文将探讨如何选择合适的技术和工具,以及推荐一些在实际开发中常用的解决方案。技术选择的原则平衡压缩率与视频质量:在选择压缩技术时,需要平衡压缩率与视频质量之间的关系。过......
  • 计算机组成原理历年考研真题对应知识点(数制与编码)
    目录2.1数制与编码2.1.1进位计数制及其相互转换【命题追踪——采用二进制编码的原因(2018)】【命题追踪——十进制小数转换为二进制小数(2021、2022)】2.1.2定点数的编码表示【命题追踪——补码的表示范围(2010、2013、2014、2022)】【命题追踪——补码和真值的相互转......
  • 关于加密,解密,摘要,编码的理解和应用
    故事的开始那是一个夏天,在杭州,和两位好友吃完饭聊着。他们都是刚刚入行的程序员,讨论着密码在系统中存储的方式MD5.当时的我还是个门外汉,听着他们的讨论,非常有兴趣。那时候我认为MD5是加密放方式(当然现在看来不是)。那时候我认为处理密码的方式是,用户注册后,密码通过MD5加盐方式存入......
  • 网址URL中特殊字符转义编码
    网址URL中特殊字符转义编码字符URL编码值空格%20"%22%23%%25&%26(%28)%29%2B,%2C/%2F:%3A;......
  • 用于将字节进行base64编码或解码(C语言实现)
    V1.02024年6月13日发布于博客园目录base64.hbase64.c基本原理见代码注释!base64.h#ifndef_BASE64_H#define_BASE64_H/***@filename:base64.h*@brief:用于将字节进行base64编码或解码*@author:[email protected]*@date:2024年6......
  • 数据结构复习笔记5.6:哈夫曼编码树
    1.前导概念1.定义:设有n个权值{w1,w2,…,wn},构造一棵有n个叶子结点的二叉树,每个叶子的权值为wi,则wpl最小的二叉树叫哈夫曼树。例子:2.结点的路径长度:从根结点到该结点的路径上的连接数3.树的路径长度:就是树的每个叶⼦结点的路径⻓度之和4.结点的带权路径⻓度:结点的路径⻓......
  • Base64编码解码流程的初步学习
    目录什么是Base64编码?为什么要学习Base64编码?Base64编码基础原理介绍Base64编码组成Base64编码索引表Base64编码规则Base64编码过程简记编码流程实战Base64编码(不同情况举例说明)1.待编码字符数量为3的倍数2.待编码字符数量不为3的倍数Base64解码原理简单介绍Base64解码过程Base6......