Proceso RDO de división de bloques intratrama x265

1. CU, PU, ​​TU dentro del marco

CU: Unidad de codificación, el tamaño máximo de CU en el marco en H.265 es 64x64, el tamaño mínimo es 8x8 y solo puede ser un bloque cuadrado

PU: unidad de predicción, H.265 PU tiene dos tipos:

  • SIZE_2Nx2N: No es necesario dividir más la CU actual al hacer predicciones, y el tamaño de la PU es igual al tamaño de la CU
  • SIZE_NxN: solo 8x8 CU tienen este método de división, es decir, el CU actual se divide recursivamente en 4 4x4 sub-PU utilizando un quadtree durante la predicción

TU: Unidad de transformación, soporta 32x32, 16x16, 8x8, 4x4

La relación entre PU y TU: debido a que PU y TU están directamente divididos por CU, no existe una relación definida entre sus tamaños. Una PU puede contener varias TU y una TU puede abarcar varias PU, pero el tamaño de las dos debe ser más pequeño que CU. Para la codificación intratrama, debido a la dependencia entre PU adyacentes, es decir, la PU actual debe hacer referencia a las PU codificadas adyacentes al realizar predicciones. Por lo tanto, una PU puede contener varias TU, pero una TU solo puede corresponder a una PU a lo sumo.

2. Proceso de división de bloques intracuadro en x265z

En x265, es el proceso RDO de división de bloques de bloques intratrama en la función compressIntraCU . Al dividir, se realiza de forma recursiva y H.265 solo admite la división de cuatro árboles, como se muestra en la siguiente figura como ejemplo.

 El proceso de división de 64x64 CU es el siguiente:

  1. Inicie la división desde la raíz 64x64CU y obtenga la primera CU 32x32 a través de la división de cuatro árboles
  2. Para las primeras 32x32 CU, primero llame a la función checkIntra para realizar el RDO del modo de predicción intra y calcule el costo RD; divida las 32x32 CU en un quadtree para obtener 4 16x16 CU
  3. Para las primeras CU 16x16, primero llame a la función checkIntra para realizar el RDO del modo de predicción intra y calcular el Costo RD; divida las CU 16x16 en un quadtree para obtener 4 CU 8x8
  4. Para cuatro 8x8 CU, llame a la función checkIntra para cada 8x8CU para calcular el costo de RD
  5. Regrese a las CU 16x16 en el tercer paso y compare el Costo RD obtenido sin división de cuatro árboles con el Costo RD obtenido en el cuarto paso. El resultado de la comparación entre los dos determina si las CU 16x16 se dividen en 4 8x8 CU
  6. De la misma manera, compare la segunda, tercera y cuarta CU de 16x16 y sume los costos de RD óptimos de estas cuatro 16x16CU.
  7. Regrese a las 32x32 CU en el segundo paso, compare el Costo RD del primer 32x32CU y la suma acumulada de los cuatro Costos RD 16x16 obtenidos en el sexto, decidiendo así dividir la 32x32CU en un quadtree
  8. De la misma manera, calcule el costo de RD óptimo del segundo, tercero y cuarto 32x32CU, y decida si lo dividirá en un árbol cuádruple.

(Nota: en teoría, el número máximo de CU intracuadro debería ser 64x64, pero entiendo por el código en x265 que no hay CU intracuadro 64x64, espero que alguien pueda corregirme)

Referencia de RDO para el modo de predicción intra: https://blog.csdn.net/BigDream123/article/details/112383895

El código y los comentarios son los siguientes

uint64_t Analysis::compressIntraCU(const CUData& parentCTU, const CUGeom& cuGeom, int32_t qp)
{
    uint32_t depth = cuGeom.depth;
    ModeDepth& md = m_modeDepth[depth];
    md.bestMode = NULL;

    bool mightSplit = !(cuGeom.flags & CUGeom::LEAF);
    bool mightNotSplit = !(cuGeom.flags & CUGeom::SPLIT_MANDATORY);

    bool bAlreadyDecided = m_param->intraRefine != 4 && parentCTU.m_lumaIntraDir[cuGeom.absPartIdx] != (uint8_t)ALL_IDX && !(m_param->bAnalysisType == HEVC_INFO);
    bool bDecidedDepth = m_param->intraRefine != 4 && parentCTU.m_cuDepth[cuGeom.absPartIdx] == depth;
    int split = 0;
    if (m_param->intraRefine && m_param->intraRefine != 4)
    {
        split = m_param->scaleFactor && bDecidedDepth && (!mightNotSplit || 
            ((cuGeom.log2CUSize == (uint32_t)(g_log2Size[m_param->minCUSize] + 1))));
        if (cuGeom.log2CUSize == (uint32_t)(g_log2Size[m_param->minCUSize]) && !bDecidedDepth)
            bAlreadyDecided = false;
    }

    if (bAlreadyDecided)
    {
        if (bDecidedDepth && mightNotSplit)
        {
            Mode& mode = md.pred[0];
            md.bestMode = &mode;
            mode.cu.initSubCU(parentCTU, cuGeom, qp);
            bool reuseModes = !((m_param->intraRefine == 3) ||
                                (m_param->intraRefine == 2 && parentCTU.m_lumaIntraDir[cuGeom.absPartIdx] > DC_IDX));
            if (reuseModes)
            {
                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]);

            if (m_bTryLossless)
                tryLossless(cuGeom);

            if (mightSplit)
                addSplitFlagCost(*md.bestMode, cuGeom.depth);
        }
    }
    else if (cuGeom.log2CUSize != MAX_LOG2_CU_SIZE && mightNotSplit)
    {   // 如果当前尺寸不等于最大CU尺寸(64x64)且可能不会继续划分,则开始选择预测模式
        md.pred[PRED_INTRA].cu.initSubCU(parentCTU, cuGeom, qp);
        checkIntra(md.pred[PRED_INTRA], cuGeom, SIZE_2Nx2N);
        checkBestMode(md.pred[PRED_INTRA], depth);
		
        if (cuGeom.log2CUSize == 3 && m_slice->m_sps->quadtreeTULog2MinSize < 3)
        {	// 如果当前CU尺寸为8x8,则计算将CU划分为4个4x4 PU进行预测所需的RD Cost
            md.pred[PRED_INTRA_NxN].cu.initSubCU(parentCTU, cuGeom, qp);
            checkIntra(md.pred[PRED_INTRA_NxN], cuGeom, SIZE_NxN);
            checkBestMode(md.pred[PRED_INTRA_NxN], depth);
        }

        if (m_bTryLossless)
            tryLossless(cuGeom);

        if (mightSplit)
            addSplitFlagCost(*md.bestMode, cuGeom.depth);
    }

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

    if (mightSplit)
    {   //如果可能继续划分,则进行递归划分
        Mode* splitPred = &md.pred[PRED_SPLIT];
        splitPred->initCosts();
        CUData* splitCU = &splitPred->cu;
        splitCU->initSubCU(parentCTU, cuGeom, qp);

        uint32_t nextDepth = depth + 1;
        ModeDepth& nd = m_modeDepth[nextDepth];
        invalidateContexts(nextDepth);
        Entropy* nextContext = &m_rqt[depth].cur;
        int32_t nextQP = qp;
        uint64_t curCost = 0;
        int skipSplitCheck = 0;

        for (uint32_t subPartIdx = 0; subPartIdx < 4; subPartIdx++)
        {
            const CUGeom& childGeom = *(&cuGeom + cuGeom.childOffset + subPartIdx);
            if (childGeom.flags & CUGeom::PRESENT)
            {
                m_modeDepth[0].fencYuv.copyPartToYuv(nd.fencYuv, childGeom.absPartIdx);
                m_rqt[nextDepth].cur.load(*nextContext);

                if (m_slice->m_pps->bUseDQP && nextDepth <= m_slice->m_pps->maxCuDQPDepth)
                    nextQP = setLambdaFromQP(parentCTU, calculateQpforCuSize(parentCTU, childGeom));

                if (m_param->bEnableSplitRdSkip)
                {
                    curCost += compressIntraCU(parentCTU, childGeom, nextQP);
					// 如果当前划分的CU的RD Cost大于总的RD Cost,则停止划分
                    if (m_modeDepth[depth].bestMode && curCost > m_modeDepth[depth].bestMode->rdCost)
                    {
                        skipSplitCheck = 1;
                        break;
                    }
                }
                else
                    compressIntraCU(parentCTU, childGeom, nextQP);

                // Save best CU and pred data for this sub CU
                splitCU->copyPartFrom(nd.bestMode->cu, childGeom, subPartIdx);
                splitPred->addSubCosts(*nd.bestMode);
                nd.bestMode->reconYuv.copyToPartYuv(splitPred->reconYuv, childGeom.numPartitions * subPartIdx);
                nextContext = &nd.bestMode->contexts;
            }
            else
            {
                /* record the depth of this non-present sub-CU */
                splitCU->setEmptyPart(childGeom, subPartIdx);

                /* Set depth of non-present CU to 0 to ensure that correct CU is fetched as reference to code deltaQP */
                if (bAlreadyDecided)
                    memset(parentCTU.m_cuDepth + childGeom.absPartIdx, 0, childGeom.numPartitions);
            }
        }
        if (!skipSplitCheck)
        {
            nextContext->store(splitPred->contexts);
            if (mightNotSplit)
                addSplitFlagCost(*splitPred, cuGeom.depth);
            else
                updateModeCost(*splitPred);

            checkDQPForSplitPred(*splitPred, cuGeom);
            checkBestMode(*splitPred, depth);
        }
    }

    if (m_param->bEnableRdRefine && depth <= m_slice->m_pps->maxCuDQPDepth)
    {
        int cuIdx = (cuGeom.childOffset - 1) / 3;
        cacheCost[cuIdx] = md.bestMode->rdCost;
    }

    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);
    if (md.bestMode != &md.pred[PRED_SPLIT])
        md.bestMode->reconYuv.copyToPicYuv(*m_frame->m_reconPic, parentCTU.m_cuAddr, cuGeom.absPartIdx);

    return md.bestMode->rdCost;
}

 

Supongo que te gusta

Origin blog.csdn.net/BigDream123/article/details/112384849
Recomendado
Clasificación