帧内色度预测编码
- 帧内预测的目的是利用图像中相邻像素的亮度和色度值之间的接近性来进行压缩。在H.264中,帧内预测包括亮度和色度的预测。色度预测模式通常是基于亮度预测模式来确定的,因为色度分量通常具有更高的空间冗余度。色度预测模式的选择可以基于亮度预测模式,并且可能包括DC模式、垂直模式、水平模式等。
- 在OpenH264中,帧内色度预测编码的过程涉及到几个核心函数,如
WelsMdIntraChroma
函数,它负责色度像素块的帧内模式决策。色度的预测模式与亮度16x16块的预测模式相似,有7种模式可供选择。在编码过程中,会根据可用的参考像素和预测模式列表来选择最佳模式,以最小化编码成本。
函数介绍
- 函数说明:类似 I16x16决策过程,色度的预测模式跟 I16x16 块一样,具体可以参考:openh264 帧内预测编码过程源码分析。
- 函数功能:针对色度像素块的帧内模式决策
- 函数原型:
int32_t WelsMdIntraChroma (SWelsFuncPtrList* pFunc, SDqLayer* pCurDqLayer, SMbCache* pMbCache, int32_t iLambda)
- 函数参数:
- pFunc: 指向函数指针列表的指针,包含用于编码的各种函数。
- pCurDqLayer: 指向当前解码层的指针。
- pMbCache: 指向宏块缓存的指针,包含编码过程中使用的缓存数据。
- iLambda: 一个用于调整成本计算的权重值。
函数关系图
函数原理
- 内部实现详细过程:
- 局部变量初始化;
- 根据宏块的领域宏块情况计算得出iOffset;
- iAvailCount 和 kpAvailMode 用于获取当前宏块可用的帧内预测模式数量和预测模式列表;
- 如果iAvailCount大于 3,且提供
pfIntra16x16Combined3
函数,
- 调用
pfIntra16x16Combined3
函数计算出最佳代价iBestCost和色度最佳模式iBestMode; - 基于当前预测模式iCurMode调用
pfGetChromaPred
函数分别预测两个色度 Cb、Cr的预测块; - 调用
pfMdCost
函数计算两个色度的代价相加,并加上iLambda * 4,作为当前模式下的代价iCurCost; - 如果 iCurCost 小于 iBestCost,
- 则更新最佳模式iBestMode和最佳代价iBestCost;
- 否则,
- 基于最佳模式iBestMode,重新调用
pfGetChromaPred
函数分别预测两个色度的预测块;
- 累加iLambda到最佳代价iBestCost;
- 否则,
- for 循环每个模式,
- 基于当前预测模式iCurMode调用
pfGetChromaPred
函数分别预测两个色度 Cb、Cr的预测块; - 调用
pfMdCost
函数计算两个色度的代价相加,并加上加上量化参数 iLambda 与当前模式编号的编码长度(使用 BsSizeUE
函数和 g_kiMapModeI16x16 数组计算)的乘积,作为当前模式下的代价iCurCost; - 如果 iCurCost 小于 iBestCost,
- 则更新最佳模式iBestMode和最佳代价iBestCost;
- 返回最佳代价iBestCost。
- 函数原理流程图:
源码
int32_t WelsMdIntraChroma (SWelsFuncPtrList* pFunc, SDqLayer* pCurDqLayer, SMbCache* pMbCache, int32_t iLambda) {
const int8_t* kpAvailMode;
int32_t iAvailCount = 0;
int32_t iChmaIdx = 0;
uint8_t* pPredIntraChma[2] = {pMbCache->pMemPredChroma, pMbCache->pMemPredChroma + 128};
uint8_t* pDstChma = pPredIntraChma[0];
uint8_t* pEncCb = pMbCache->SPicData.pEncMb[1];
uint8_t* pEncCr = pMbCache->SPicData.pEncMb[2];
uint8_t* pDecCb = pMbCache->SPicData.pCsMb[1];
uint8_t* pDecCr = pMbCache->SPicData.pCsMb[2];
const int32_t kiLineSizeEnc = pCurDqLayer->iEncStride[1];
const int32_t kiLineSizeDec = pCurDqLayer->iCsStride[1];
int32_t i, iCurMode, iCurCost, iBestMode, iBestCost = INT_MAX;
int32_t iOffset = pMbCache->uiNeighborIntra & 0x07;
iAvailCount = g_kiIntraChromaAvailMode[iOffset][4];
kpAvailMode = g_kiIntraChromaAvailMode[iOffset];
if (iAvailCount > 3 && pFunc->sSampleDealingFuncs.pfIntra8x8Combined3) {
iBestCost = pFunc->sSampleDealingFuncs.pfIntra8x8Combined3 (pDecCb, kiLineSizeDec, pEncCb, kiLineSizeEnc, &iBestMode,
iLambda, pDstChma, pDecCr, pEncCr);
iCurMode = kpAvailMode[3];
pFunc->pfGetChromaPred[iCurMode] (pDstChma, pDecCb, kiLineSizeDec);
pFunc->pfGetChromaPred[iCurMode] (pDstChma + 64, pDecCr, kiLineSizeDec);
iCurCost = pFunc->sSampleDealingFuncs.pfMdCost[BLOCK_8x8] (pDstChma, 8, pEncCb, kiLineSizeEnc) +
pFunc->sSampleDealingFuncs.pfMdCost[BLOCK_8x8] (pDstChma + 64, 8, pEncCr, kiLineSizeEnc) +
iLambda * 4;
if (iCurCost < iBestCost) {
iBestMode = iCurMode;
iBestCost = iCurCost;
} else {
pFunc->pfGetChromaPred[iBestMode] (pDstChma, pDecCb, kiLineSizeDec);
pFunc->pfGetChromaPred[iBestMode] (pDstChma + 64, pDecCr, kiLineSizeDec);
}
iBestCost += iLambda;
iChmaIdx = 1;
} else {
iBestMode = kpAvailMode[0];
for (i = 0; i < iAvailCount; ++ i) {
iCurMode = kpAvailMode[i];
assert (iCurMode >= 0 && iCurMode < 7);
pFunc->pfGetChromaPred[iCurMode] (pDstChma, pDecCb, kiLineSizeDec);
iCurCost = pFunc->sSampleDealingFuncs.pfMdCost[BLOCK_8x8] (pDstChma, 8, pEncCb, kiLineSizeEnc);
pFunc->pfGetChromaPred[iCurMode] (pDstChma + 64, pDecCr, kiLineSizeDec);
iCurCost += pFunc->sSampleDealingFuncs.pfMdCost[BLOCK_8x8] (pDstChma + 64, 8, pEncCr, kiLineSizeEnc) +
iLambda * BsSizeUE (g_kiMapModeIntraChroma[iCurMode]);
if (iCurCost < iBestCost) {
iBestMode = iCurMode;
iBestCost = iCurCost;
iChmaIdx = iChmaIdx ^ 0x01;
pDstChma = pPredIntraChma[iChmaIdx];
}
}
}
pMbCache->pBestPredIntraChroma = pPredIntraChma[iChmaIdx ^ 0x01];
pMbCache->uiChmaI8x8Mode = iBestMode;
return iBestCost;
}