Analysis::compressIntraCU()

/*
	对当前CU当前深度的四叉树递归进行帧内预测压缩分析,得到当前CU的最优预测,返回其rdcost

	过程:
		1.得到当前深度depth,以及当前深度下的modeDepth
		2.判断当前CU是否可以split?是否可以不split?若是CU是叶子节点,则不可以split;若CU强行split,则不可以不split
		3.判断当前CU是否已经decided,或深度已经decided。
			1.若intraRefine等级!=4,且当前CU加载的IPM方向不是ALL_IDX,且bAnalysisType!=HEVC,则bAlreadyDecided为真
			2.若intraRefine等级!=4,且当前CU深度已经达到了加载的load,则bDecidedDepth为真

		4.若intraRefine在1~3之间,则更新split和bAlreadyDecided标记
		5.进行非split模式的计算
			·若bAlreadyDecided,则
				1.若深度已经确定且可以不split,则
					1.初始化CU
					2.判断是否使用加载的数据
					3.若使用加载的数据,则加载
					4.针对加载的数据进行预测计算,得到rdcost
					5.若尝试lossless,则计算lossless模式下的rdcost,更新当前深度下的bestMode
					6.若可以split,则计算添加splitFlag编码开销的rdcost
			·若当前CUsize!=64x64,且可以不split,则计算INTRA_2Nx2N的开销
				1.初始化INTRA_2Nx2N
				2.计算INTRA_2Nx2N的最优预测,得到其rdcost
				3.更新当前深度下的bestMode
				4.若CUsize=8x8,且TU允许小于8x8,则计算INTRA_NxN的开销
					1.初始化INTRA_NxN
					2.计算INTRA_NxN的最优预测,得到其rdcost
					3.更新当前深度下的bestMode
				5.若尝试lossless,则计算lossless模式下的rdcost,更新当前深度下的bestMode
				6.若可以split,则计算添加splitFlag编码开销的rdcost
		6.更新是否可以split的标志,
		7.若可以split
			1.初始化cost以及CU
			2.取下一层的深度nextDepth及其modeDepth
			3.标记从nextDepth起到最深一层的熵编码上下文为无效
			4.遍历split出来的四个subCU
				1.计算subCU的几何信息
				2.对subCU递归执行压缩
					·若subCU没有完全在帧外,即会被显示出来,则
						1.取原始帧像素fencYUV
						2.加载熵编码上下文
						3.若允许CU独立QP,则计算QP
						4.递归对subCU执行当前compressIntraCU函数进行压缩
						5.保存subCU的最优预测数据、重建帧reconYUV数据,并累加该subCU的cost到总cost中
						6.更新熵编码上下文
					·若subCU完全在帧外,即一点都不会被现实出来,则标记当前subCU为空
				3.存储当前CU被split后的熵编码上下文
				4.更新整个CU的rdcost
				5.更新当前深度的bestMode
		8.保存当前深度bestMode的数据
		9.若当前深度的bestMode并不是split,则需要将bestMode的reconYUV存储下来
		10.返回当前深度的bestMode的rdcost
*/
uint64_t Analysis::compressIntraCU(const CUData& parentCTU, const CUGeom& cuGeom, int32_t qp)
{
	//当前CU的深度
    uint32_t depth = cuGeom.depth;
	//当前CU深度的modeDepth
    ModeDepth& md = m_modeDepth[depth];
	//初始化深度的bestmode = null
    md.bestMode = NULL;

	//可以split,非叶节点都可以split
    bool mightSplit = !(cuGeom.flags & CUGeom::LEAF);
	//可以不split,即不强制split的CU都可以不split
    bool mightNotSplit = !(cuGeom.flags & CUGeom::SPLIT_MANDATORY);
	//初始化split = false,即不再split
	int split = 0;

	//若intraRefine != 4 && 当前CU的帧内预测方向不是all && bAnalysisType != HEVC_INFO
    bool bAlreadyDecided = m_param->intraRefine != 4 && parentCTU.m_lumaIntraDir[cuGeom.absPartIdx] != (uint8_t)ALL_IDX && !(m_param->bAnalysisType == HEVC_INFO);
    
	//intraRefine != 4 && 当前深度达到了载入的CU深度
	bool bDecidedDepth = m_param->intraRefine != 4 && parentCTU.m_cuDepth[cuGeom.absPartIdx] == depth;
    
	//若intraRefine在 1, 2, 3之中,即使用load的数据但会进行适当修正
    if (m_param->intraRefine && m_param->intraRefine != 4)
    {
		/*	若开启了scale analysis save && 已经决定了深度 && 
			(一定要split || 当前CU的大小比允许的最小CU大小大一级)
			则要split */
        split = m_param->scaleFactor 
			&& bDecidedDepth	// depth已经decided
			&& (!mightNotSplit	// 一定要split || 深度在最小CU尺寸深度的上一级
				|| ((cuGeom.log2CUSize == (uint32_t)(g_log2Size[m_param->minCUSize] + 1))));

		//若当前CU的大小达到了允许的最小大小 && depth还没decided
        if (cuGeom.log2CUSize == (uint32_t)(g_log2Size[m_param->minCUSize]) 
			&& !bDecidedDepth)
            bAlreadyDecided = false;
    }

    if (bAlreadyDecided)	//已经决定
    {
		//depth已经decided && 可以不split
        if (bDecidedDepth && mightNotSplit)
        {
            Mode& mode = md.pred[0];
            md.bestMode = &mode;
			//初始化CU
            mode.cu.initSubCU(parentCTU, cuGeom, qp);
			//是否使用加载的预测信息,即 既不intraRefine == 3,也不(intraRefine == 3 && 加载的CU帧内预测方向为angle类型)
            bool reuseModes = !((m_param->intraRefine == 3) ||
                                (m_param->intraRefine == 2 && parentCTU.m_lumaIntraDir[cuGeom.absPartIdx] > DC_IDX));
            //若使用加载的预测信息,则加载它们
			if (reuseModes)
            {
				//拷贝luma和chroma的intra direction
                memcpy(mode.cu.m_lumaIntraDir, parentCTU.m_lumaIntraDir + cuGeom.absPartIdx, cuGeom.numPartitions);
                memcpy(mode.cu.m_chromaIntraDir, parentCTU.m_chromaIntraDir + cuGeom.absPartIdx, cuGeom.numPartitions);
            }
			//计算开销
            checkIntra(mode, cuGeom, (PartSize)parentCTU.m_partSize[cuGeom.absPartIdx]);

			//若bTryLossless,则尝试无损编码
            if (m_bTryLossless)
                tryLossless(cuGeom);
			//可以split,则计算添加splitFlag后的rdcost
            if (mightSplit)
                addSplitFlagCost(*md.bestMode, cuGeom.depth);
        }
    }
	//若不是64x64 && 可以不split
	else if (cuGeom.log2CUSize != MAX_LOG2_CU_SIZE && mightNotSplit)
    {
		/*
			计算不split的Intra2Nx2N开销
		*/
		//初始化intra
        md.pred[PRED_INTRA].cu.initSubCU(parentCTU, cuGeom, qp);
		//计算Intra2Nx2N的rdcost
        checkIntra(md.pred[PRED_INTRA], cuGeom, SIZE_2Nx2N);
        //更新bestMode
		checkBestMode(md.pred[PRED_INTRA], depth);
		
		//若是CU是8x8 && TU允许小于8x8
        if (cuGeom.log2CUSize == 3 && m_slice->m_sps->quadtreeTULog2MinSize < 3)
        {
			/*
				计算8x8CU中的4x4PU模式
			*/
			//初始化intra NxN
            md.pred[PRED_INTRA_NxN].cu.initSubCU(parentCTU, cuGeom, qp);
			//计算intra NxN的rdcost
            checkIntra(md.pred[PRED_INTRA_NxN], cuGeom, SIZE_NxN);
			//更新bestMode
            checkBestMode(md.pred[PRED_INTRA_NxN], depth);
        }

		//若bTryLossless,则计算lossless模式,并更新bestMode
        if (m_bTryLossless)
            tryLossless(cuGeom);

		//可以split,则计算添加splitFlag后的rdcost
        if (mightSplit)
            addSplitFlagCost(*md.bestMode, cuGeom.depth);
    }

    /* stop recursion if we reach the depth of previous analysis decision
	   如果我们达到之前分析的深度,停止递归split
	   没有decided || 没有决定深度 || 可以split*/
    mightSplit &= !(bAlreadyDecided && bDecidedDepth) || split;

	//若可以split,则递归对subBlock进行压缩分析
    if (mightSplit)
    {
		//取split的mode
        Mode* splitPred = &md.pred[PRED_SPLIT];
		//初始化split模式下的cost
        splitPred->initCosts();
		//取split模式下的CUdata
        CUData* splitCU = &splitPred->cu;
		//初始化子CU
        splitCU->initSubCU(parentCTU, cuGeom, qp);

		//nextDepth
        uint32_t nextDepth = depth + 1;
		//根据nextDepth得到ModeDepth
        ModeDepth& nd = m_modeDepth[nextDepth];
		//无效nextDepth深度一直到最深的四叉树上下文
        invalidateContexts(nextDepth);
		//将当前四叉树的上下文作为下一深度的上下文
        Entropy* nextContext = &m_rqt[depth].cur;
		//当前qp作为下一深度的qp
        int32_t nextQP = qp;
		//初始化curCost为0,curCost累加四个subBlock的cost
        uint64_t curCost = 0;
		//初始化skipSplitCheck为0
        int skipSplitCheck = 0;

		//进行四个subCU遍历
        for (uint32_t subPartIdx = 0; subPartIdx < 4; subPartIdx++)	//遍历4个subCU
        {
			//得到subCU的CUGeom
            const CUGeom& childGeom = *(&cuGeom + cuGeom.childOffset + subPartIdx);

			//若subCU没有完全在帧外
            if (childGeom.flags & CUGeom::PRESENT)
            {
				//取fencYUV
                m_modeDepth[0].fencYuv.copyPartToYuv(nd.fencYuv, childGeom.absPartIdx);
                //加载上下文
				m_rqt[nextDepth].cur.load(*nextContext);

				//若使用DQP(CU独立QP),且深度在最大的允许DQP深度内,则进行独立QP
                if (m_slice->m_pps->bUseDQP && nextDepth <= m_slice->m_pps->maxCuDQPDepth)
                    nextQP = setLambdaFromQP(parentCTU, calculateQpforCuSize(parentCTU, childGeom));

				/*
					递归进行CU压缩
				*/
				//若允许bEnableSplitRdSkip,即累加subCU过程中一旦超过bestMode,立马停止
                if (m_param->bEnableSplitRdSkip)
                {
					//累加subBlock的rdCost
                    curCost += compressIntraCU(parentCTU, childGeom, nextQP);
					//若累加的rdCost > 当前bestMode的rdCost,则break停止计算,标记skipSplitCheck
                    if (m_modeDepth[depth].bestMode && curCost > m_modeDepth[depth].bestMode->rdCost)
                    {
                        skipSplitCheck = 1;
                        break;
                    }
                }
                else //不允许bEnableSplitRdSkip,则老老实实计算所有子CU
                    compressIntraCU(parentCTU, childGeom, nextQP);

                // Save best CU and pred data for this sub CU 记录下subBlock的最优预测数据
                splitCU->copyPartFrom(nd.bestMode->cu, childGeom, subPartIdx);
				//添加其cost
                splitPred->addSubCosts(*nd.bestMode);
				//将bestMode下的reconYUV保存下来
                nd.bestMode->reconYuv.copyToPartYuv(splitPred->reconYuv, childGeom.numPartitions * subPartIdx);
                //更新上下文
				nextContext = &nd.bestMode->contexts;
            }
            else //subCU完全在帧外,则不用进行计算了
            {
                /* record the depth of this non-present sub-CU 
				   记录深度,该subBlock不显示,设置为空*/
                splitCU->setEmptyPart(childGeom, subPartIdx);

                /* Set depth of non-present CU to 0 to ensure that correct CU is fetched as reference to code deltaQP 
				   将它们的cuDepth置0 */
                if (bAlreadyDecided)
                    memset(parentCTU.m_cuDepth + childGeom.absPartIdx, 0, childGeom.numPartitions);
            }
        } // end of loop over subPartIdx

		/*若之前发生了累计4个subCU的开销>bestMode的开销时,早退的情况,则跳过这里
		  否则要进行SplitCheck,检查split是否比不split开销小  */
		if (!skipSplitCheck)
        {
			//存储当前CU被split后的熵编码上下文
            nextContext->store(splitPred->contexts);
			//更新rdcost
            if (mightNotSplit)
                addSplitFlagCost(*splitPred, cuGeom.depth);
            else
                updateModeCost(*splitPred);

            checkDQPForSplitPred(*splitPred, cuGeom);
			//更新bestMode
            checkBestMode(*splitPred, depth);
        }
    } //end of if(mightSplit)

	//若允许RD优化且深度在最大DQP深度内,则缓存rdcost
    if (m_param->bEnableRdRefine && depth <= m_slice->m_pps->maxCuDQPDepth)
    {
        int cuIdx = (cuGeom.childOffset - 1) / 3;
        cacheCost[cuIdx] = md.bestMode->rdCost;
    }

	//若CU大小在16x16及以上,且
    if ((m_limitTU & X265_TU_LIMIT_NEIGH) && cuGeom.log2CUSize >= 4)
    {
        CUData* ctu = md.bestMode->cu.m_encData->getPicCTU(parentCTU.m_cuAddr);
        int8_t maxTUDepth = -1;
        for (uint32_t i = 0; i < cuGeom.numPartitions; i++)
            maxTUDepth = X265_MAX(maxTUDepth, md.bestMode->cu.m_tuDepth[i]);
        ctu->m_refTuDepth[cuGeom.geomRecurId] = maxTUDepth;
    }

    /* Copy best data to encData CTU and recon */
    md.bestMode->cu.copyToPic(depth);
	//若当前深度bestMode不是split,则
    if (md.bestMode != &md.pred[PRED_SPLIT])
        md.bestMode->reconYuv.copyToPicYuv(*m_frame->m_reconPic, parentCTU.m_cuAddr, cuGeom.absPartIdx);

	//返回当前深度下最优mode的rdCost
    return md.bestMode->rdCost;
}

你可能感兴趣的:(X265)