粗粒度率失真计算:
rdcost = satd(fenc, pred) + lambda * IPM_bits,其中satd在一定程度上表示了频域的能量,弥补了IPM_bits没有计算残差系数等bits开销的不足,该方法计算/时间开销小,因为没有进行变换/量化/反量化/反变换等过程;但是其结果只具有一定代表性,其计算的最优可能并不一定是严格意义上的最优,而只是可能较优。
细粒度率失真计算:
rdcost = sse(fenc, recon) + lambda * all_bits,该方法严格计算了原始帧和重建帧之间的distortion,并对执行了整个编码流程,包括变换/量化/反量化/反变换等等,是真正意义上的拉格朗日码控计算,其计算的最优就是严格意义上的最优,但是计算成本大。
X265在分析最优帧内预测方向中,采用了两种结合的方式,先使用粗粒度计算方式得到一些可能的最优帧内预测方向备选集,然后在这些备选集中用细粒度计算方式得到最后严格意义上的最优帧内预测方向
/*
为当前CU中各个PU分析最优的帧内预测方向,并返回整个CU的distortion
过程:
1.获取depth、initTuDepth、TUsize、PU个数等信息
2.检查是否TransformSkip
3.遍历当前CU的所有PU
1.对当前PU分析其最优帧内预测方向
·若指定了帧内预测方向,则直接将其定为最优帧内预测方向
·否则,进行最优帧内预测方向选择
1.获取相邻PU参考像素可用信息
2.对相邻PU参考像素信息进行填充并平滑滤波
3.加载3个mpms,并得到未命中mpms时的bits开销
4.进行DC帧内预测方向计算
1.进行DC帧内预测
2.得到编码DC帧内预测方向的mode_bits
3.计算distortion = sa8d(fenc, pred)
4.计算存储cost[DC] = distortion + lambda * mode_bits,并将其设置为最优开销bcost
5.进行PLANAR帧内预测方向计算
1.进行PLANAR帧内预测,TUsize在8~32内用平滑滤波后的参考像素,否则使用未滤波的像素
2.得到编码PLANAR帧内预测方向的mode_bits
3.计算distortion = sa8d(fenc, pred)
4.计算存储cost[PLANAR]= distortion + lambda * mode_bits,并基于cost更新bcost
6.进行angle2~34帧内预测方向计算
·若intra_pred_allangs函数定义,则
1.转置fenc矩阵为fenc^
2.进行intra_pred_allangs函数计算,输出angle2~34一共33种预测方向的预测像素
3.遍历angle2~34
1.得到编码当前angle下帧内预测方向的mode_bits
2.计算distortion
·若angle在2~18中,即从水平向右的所有帧内预测方向,则distortion = satd(fenc^, pred)
·否则,即angle在19~34中,也就是垂直向下的那些帧内预测方向,则distortion = satd(fenc, pred)
3.计算cost[angle] = distortion + lambda * mode_bits
·若没有intra_pred_allangs函数定义,则遍历angle2~34帧内预测方向
1.得到编码当前angle下帧内预测方向的mode_bits
2.判断是否使用平滑滤波后的参考像素
3.计算distortion = sa8d(fenc, pred)
4.计算cost[angle] = distortion + lambda * mode_bits
7.选取最多maxCandCount个cost在1.25倍bcost内的帧内预测方向作为帧内预测方向备选集cand
8.遍历所有cand,在cand中寻找严格意义上的最优
1.加载熵编码上下文,并设置好帧内预测方向
2.针对指定的帧内预测方向,严格基于rdcost = sse(fenc, recon) + lambda * all_bits,确定最优的TU划分,并得到rdcost、bits、distortion、energy开销
3.基于rdcost来更新最优开销bcost以及最优帧内预测方向bmode
2.设置得到的最优帧内预测方向
3.载入熵编码上下文
4.再次调用codeIntraLumaTSkip/codeIntraLumaQT来重新得到其残差系数、reconYUV、以及一些开销
5.累加当前PU最优预测方向的distortion到totalDistortion中
6.提取存储保留最优帧内预测方向的残差系数和reconYUV
7.若当前PU不是当前CU的最后一块PU,则保留reconYUV,为下一PU的帧内预测做参考
8.若当前CU划分了多个PU,则merge各个PU的cbf
9.返回totalDistortion
*/
sse_t Search::estIntraPredQT(Mode &intraMode, const CUGeom& cuGeom, const uint32_t depthRange[2])
{
CUData& cu = intraMode.cu;
//原始帧、预测帧、重建帧
const Yuv* fencYuv = intraMode.fencYuv;
Yuv* predYuv = &intraMode.predYuv;
Yuv* reconYuv = &intraMode.reconYuv;
uint32_t depth = cuGeom.depth; //CU深度
uint32_t initTuDepth = cu.m_partSize[0] != SIZE_2Nx2N; //初始TU深度,2Nx2N=>深度0,NxN=>深度1
uint32_t numPU = 1 << (2 * initTuDepth); //PU个数,2Nx2N=>1个,NxN=>4个
uint32_t log2TrSize = cuGeom.log2CUSize - initTuDepth;//TUsize,单位log(pixel)
uint32_t tuSize = 1 << log2TrSize; //TUsize,单位pixel
uint32_t qNumParts = cuGeom.numPartitions >> 2;
uint32_t sizeIdx = log2TrSize - 2;
uint32_t absPartIdx = 0;
sse_t totalDistortion = 0;
//是否跳过transform
int checkTransformSkip = m_slice->m_pps->bTransformSkipEnabled && !cu.m_tqBypass[0] && cu.m_partSize[0] != SIZE_2Nx2N;
// loop over partitions 遍历所有PU
for (uint32_t puIdx = 0; puIdx < numPU; puIdx++, absPartIdx += qNumParts)
{
uint32_t bmode = 0;
//若指定了帧内预测方向,即非ALL_IDX,则不用进行帧内预测方向分析了
if (intraMode.cu.m_lumaIntraDir[puIdx] != (uint8_t)ALL_IDX)
bmode = intraMode.cu.m_lumaIntraDir[puIdx];
//否则,进行最优帧内预测方向计算
else
{
uint64_t candCostList[MAX_RD_INTRA_MODES];
uint32_t rdModeList[MAX_RD_INTRA_MODES];
uint64_t bcost;
int maxCandCount = 2 + m_param->rdLevel +