Skip to content
Snippets Groups Projects
EncGOP.cpp 186 KiB
Newer Older
  • Learn to ignore specific revisions
  •       //get complementary top field
          PicList::iterator   iterPic = rcListPic.begin();
          while ((*iterPic)->getPOC() != correspondingFieldPOC)
          {
            iterPic ++;
          }
          Picture* correspondingFieldPic = *(iterPic);
    
    
          if ((pcPic->topField && isFieldTopFieldFirst) || (!pcPic->topField && !isFieldTopFieldFirst))
    
            xCalculateInterlacedAddPSNR(pcPic, correspondingFieldPic, pcPic->getRecoBuf(), correspondingFieldPic->getRecoBuf(), snr_conversion, printFrameMSE, PSNR_Y
              , isEncodeLtRef
            );
    
            xCalculateInterlacedAddPSNR(correspondingFieldPic, pcPic, correspondingFieldPic->getRecoBuf(), pcPic->getRecoBuf(), snr_conversion, printFrameMSE, PSNR_Y
              , isEncodeLtRef
            );
    
    void EncGOP::xCalculateAddPSNR(Picture* pcPic, PelUnitBuf cPicD, const AccessUnit& accessUnit, double dEncTime, const InputColourSpaceConversion conversion, const bool printFrameMSE, double* PSNR_Y
                                  , bool isEncodeLtRef
    )
    
    {
      const SPS&         sps = *pcPic->cs->sps;
      const CPelUnitBuf& pic = cPicD;
      CHECK(!(conversion == IPCOLOURSPACE_UNCHANGED), "Unspecified error");
    //  const CPelUnitBuf& org = (conversion != IPCOLOURSPACE_UNCHANGED) ? pcPic->getPicYuvTrueOrg()->getBuf() : pcPic->getPicYuvOrg()->getBuf();
    
    Taoran Lu's avatar
    Taoran Lu committed
      const CPelUnitBuf& org = sps.getUseReshaper() ? pcPic->getTrueOrigBuf() : pcPic->getOrigBuf();
    
    #if ENABLE_QPA
      const bool    useWPSNR = m_pcEncLib->getUseWPSNR();
    #endif
      double  dPSNR[MAX_NUM_COMPONENT];
    #if WCG_WPSNR
    
    Taoran Lu's avatar
    Taoran Lu committed
      const bool    useLumaWPSNR = m_pcEncLib->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcCfg->getReshaper() && m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ);
    
      double  dPSNRWeighted[MAX_NUM_COMPONENT];
      double  MSEyuvframeWeighted[MAX_NUM_COMPONENT];
    
    #endif
    #if RPR_CTC_PRINT
      double  upscaledPSNR[MAX_NUM_COMPONENT];
    
    #endif
      for(int i=0; i<MAX_NUM_COMPONENT; i++)
      {
        dPSNR[i]=0.0;
    #if WCG_WPSNR
        dPSNRWeighted[i]=0.0;
        MSEyuvframeWeighted[i] = 0.0;
    
    #endif
    #if RPR_CTC_PRINT
        upscaledPSNR[i] = 0.0;
    
    #if JVET_O0756_CALCULATE_HDRMETRICS
      double deltaE[hdrtoolslib::NB_REF_WHITE];
      double psnrL[hdrtoolslib::NB_REF_WHITE];
      for (int i=0; i<hdrtoolslib::NB_REF_WHITE; i++)
      {
        deltaE[i] = 0.0;
        psnrL[i] = 0.0;
    
      PelStorage interm;
    
      if (conversion != IPCOLOURSPACE_UNCHANGED)
      {
        interm.create(pic.chromaFormat, Area(Position(), pic.Y()));
        VideoIOYuv::ColourSpaceConvert(pic, interm, conversion, false);
      }
    
      const CPelUnitBuf& picC = (conversion == IPCOLOURSPACE_UNCHANGED) ? pic : interm;
    
      //===== calculate PSNR =====
      double MSEyuvframe[MAX_NUM_COMPONENT] = {0, 0, 0};
      const ChromaFormat formatD = pic.chromaFormat;
      const ChromaFormat format  = sps.getChromaFormatIdc();
    
      const bool bPicIsField     = pcPic->fieldPic;
      const Slice*  pcSlice      = pcPic->slices[0];
    
    
    #if RPR_CTC_PRINT
      PelStorage upscaledRec;
    
      if( m_pcEncLib->isRPREnabled() )
      {
    
        const CPelBuf& upscaledOrg = sps.getUseReshaper() ? pcPic->M_BUFS( 0, PIC_TRUE_ORIGINAL_INPUT).get( COMPONENT_Y ) : pcPic->M_BUFS( 0, PIC_ORIGINAL_INPUT).get( COMPONENT_Y );
    
        upscaledRec.create( pic.chromaFormat, Area( Position(), upscaledOrg ) );
    
    #if RPR_CONF_WINDOW
    
        // the input source picture has a conformance window derived at encoder
        Window& conformanceWindow = m_pcEncLib->getConformanceWindow();
    
        Picture::rescalePicture( picC, pcPic->cs->pps->getConformanceWindow(), upscaledRec, conformanceWindow, format, sps.getBitDepths(), false );
    #else
        Picture::rescalePicture(picC, upscaledRec, format, sps.getBitDepths(), false);
    #endif
    
      for (int comp = 0; comp < ::getNumberValidComponents(formatD); comp++)
      {
        const ComponentID compID = ComponentID(comp);
        const CPelBuf&    p = picC.get(compID);
        const CPelBuf&    o = org.get(compID);
    
        CHECK(!( p.width  == o.width), "Unspecified error");
        CHECK(!( p.height == o.height), "Unspecified error");
    
        const uint32_t   width  = p.width  - (m_pcEncLib->getPad(0) >> ::getComponentScaleX(compID, format));
        const uint32_t   height = p.height - (m_pcEncLib->getPad(1) >> (!!bPicIsField+::getComponentScaleY(compID,format)));
    
        // create new buffers with correct dimensions
        const CPelBuf recPB(p.bufAt(0, 0), p.stride, width, height);
        const CPelBuf orgPB(o.bufAt(0, 0), o.stride, width, height);
        const uint32_t    bitDepth = sps.getBitDepth(toChannelType(compID));
    #if ENABLE_QPA
        const uint64_t uiSSDtemp = xFindDistortionPlane(recPB, orgPB, useWPSNR ? bitDepth : 0, ::getComponentScaleX(compID, format));
    #else
        const uint64_t uiSSDtemp = xFindDistortionPlane(recPB, orgPB, 0);
    #endif
    
        const uint32_t size   = width * height;
        const double fRefValue = (double)maxval * maxval * size;
        dPSNR[comp]       = uiSSDtemp ? 10.0 * log10(fRefValue / (double)uiSSDtemp) : 999.99;
        MSEyuvframe[comp] = (double)uiSSDtemp / size;
    #if WCG_WPSNR
    
        const double uiSSDtempWeighted = xFindDistortionPlaneWPSNR(recPB, orgPB, 0, org.get(COMPONENT_Y), compID, format);
    
        if (useLumaWPSNR)
        {
          dPSNRWeighted[comp] = uiSSDtempWeighted ? 10.0 * log10(fRefValue / (double)uiSSDtempWeighted) : 999.99;
          MSEyuvframeWeighted[comp] = (double)uiSSDtempWeighted / size;
        }
    #endif
    
    
    #if RPR_CTC_PRINT
        if( m_pcEncLib->isRPREnabled() )
        {
    
          const CPelBuf& upscaledOrg = sps.getUseReshaper() ? pcPic->M_BUFS( 0, PIC_TRUE_ORIGINAL_INPUT ).get( compID ) : pcPic->M_BUFS( 0, PIC_ORIGINAL_INPUT ).get( compID );
    
    
    #if ENABLE_QPA
          const uint64_t upscaledSSD = xFindDistortionPlane( upscaledRec.get( compID ), upscaledOrg, useWPSNR ? bitDepth : 0, ::getComponentScaleX( compID, format ) );
    #else
          const uint64_t scaledSSD = xFindDistortionPlane( upscaledRec.get( compID ), upscaledOrg, 0 );
    #endif
    
          upscaledPSNR[comp] = upscaledSSD ? 10.0 * log10( (double)maxval * maxval * upscaledOrg.width * upscaledOrg.height / (double)upscaledSSD ) : 999.99;
        }
    #endif
    
      }
    
    #if EXTENSION_360_VIDEO
      m_ext360.calculatePSNRs(pcPic);
    #endif
    
    #if JVET_O0756_CALCULATE_HDRMETRICS
      const bool calculateHdrMetrics = m_pcEncLib->getCalcluateHdrMetrics();
    
      {
        auto beforeTime = std::chrono::steady_clock::now();
    
        xCalculateHDRMetrics(pcPic, deltaE, psnrL);
    
        auto elapsed = std::chrono::steady_clock::now() - beforeTime;
        m_metricTime += elapsed;
      }
    #endif
    
      /* calculate the size of the access unit, excluding:
       *  - any AnnexB contributions (start_code_prefix, zero_byte, etc.,)
       *  - SEI NAL units
       */
      uint32_t numRBSPBytes = 0;
      for (AccessUnit::const_iterator it = accessUnit.begin(); it != accessUnit.end(); it++)
      {
        uint32_t numRBSPBytes_nal = uint32_t((*it)->m_nalUnitData.str().size());
        if (m_pcCfg->getSummaryVerboseness() > 0)
        {
          msg( NOTICE, "*** %6s numBytesInNALunit: %u\n", nalUnitTypeToString((*it)->m_nalUnitType), numRBSPBytes_nal);
        }
        if( ( *it )->m_nalUnitType != NAL_UNIT_PREFIX_SEI && ( *it )->m_nalUnitType != NAL_UNIT_SUFFIX_SEI )
        {
          numRBSPBytes += numRBSPBytes_nal;
    
    Lidong Xu's avatar
    Lidong Xu committed
          if (it == accessUnit.begin() || (*it)->m_nalUnitType == NAL_UNIT_VPS || (*it)->m_nalUnitType == NAL_UNIT_DPS || (*it)->m_nalUnitType == NAL_UNIT_SPS || (*it)->m_nalUnitType == NAL_UNIT_PPS)
    
          {
            numRBSPBytes += 4;
          }
          else
          {
            numRBSPBytes += 3;
          }
        }
      }
    
      uint32_t uibits = numRBSPBytes * 8;
      m_vRVM_RP.push_back( uibits );
    
      //===== add PSNR =====
    
      m_gcAnalyzeAll.addResult(dPSNR, (double)uibits, MSEyuvframe
    
    #if EXTENSION_360_VIDEO
      m_ext360.addResult(m_gcAnalyzeAll);
    #endif
    
    #if JVET_O0756_CALCULATE_HDRMETRICS
      if (calculateHdrMetrics)
      {
        m_gcAnalyzeAll.addHDRMetricsResult(deltaE, psnrL);
    
        m_gcAnalyzeI.addResult(dPSNR, (double)uibits, MSEyuvframe
    
        *PSNR_Y = dPSNR[COMPONENT_Y];
    #if EXTENSION_360_VIDEO
        m_ext360.addResult(m_gcAnalyzeI);
    #endif
    
    #if JVET_O0756_CALCULATE_HDRMETRICS
        if (calculateHdrMetrics)
        {
          m_gcAnalyzeI.addHDRMetricsResult(deltaE, psnrL);
    
        m_gcAnalyzeP.addResult(dPSNR, (double)uibits, MSEyuvframe
    
        *PSNR_Y = dPSNR[COMPONENT_Y];
    #if EXTENSION_360_VIDEO
        m_ext360.addResult(m_gcAnalyzeP);
    #endif
    
    #if JVET_O0756_CALCULATE_HDRMETRICS
        if (calculateHdrMetrics)
        {
          m_gcAnalyzeP.addHDRMetricsResult(deltaE, psnrL);
    
        m_gcAnalyzeB.addResult(dPSNR, (double)uibits, MSEyuvframe
    
        *PSNR_Y = dPSNR[COMPONENT_Y];
    #if EXTENSION_360_VIDEO
        m_ext360.addResult(m_gcAnalyzeB);
    #endif
    
    #if JVET_O0756_CALCULATE_HDRMETRICS
        if (calculateHdrMetrics)
        {
          m_gcAnalyzeB.addHDRMetricsResult(deltaE, psnrL);
    
    #if RPR_CTC_PRINT
        m_gcAnalyzeWPSNR.addResult( dPSNRWeighted, (double)uibits, MSEyuvframeWeighted, upscaledPSNR, isEncodeLtRef );
    #else
    
        m_gcAnalyzeWPSNR.addResult(dPSNRWeighted, (double)uibits, MSEyuvframeWeighted, isEncodeLtRef);
    
      }
    #endif
    
      char c = (pcSlice->isIntra() ? 'I' : pcSlice->isInterP() ? 'P' : 'B');
      if (! pcPic->referenced)
      {
        c += 32;
      }
    
    #if JVET_N0494_DRAP
      if (m_pcCfg->getDependentRAPIndicationSEIEnabled() && pcSlice->isDRAP()) c = 'D';
    #endif
    
    
      if( g_verbosity >= NOTICE )
      {
        msg( NOTICE, "POC %4d TId: %1d ( %c-SLICE, QP %d ) %10d bits",
    
             pcSlice->getPOC(),
    
             pcSlice->getTLayer(),
             c,
             pcSlice->getSliceQp(),
             uibits );
    
        msg( NOTICE, " [Y %6.4lf dB    U %6.4lf dB    V %6.4lf dB]", dPSNR[COMPONENT_Y], dPSNR[COMPONENT_Cb], dPSNR[COMPONENT_Cr] );
    
    
    #if EXTENSION_360_VIDEO
        m_ext360.printPerPOCInfo(NOTICE);
    #endif
    
    
        if (m_pcEncLib->getPrintHexPsnr())
        {
          uint64_t xPsnr[MAX_NUM_COMPONENT];
          for (int i = 0; i < MAX_NUM_COMPONENT; i++)
          {
            copy(reinterpret_cast<uint8_t *>(&dPSNR[i]),
                 reinterpret_cast<uint8_t *>(&dPSNR[i]) + sizeof(dPSNR[i]),
                 reinterpret_cast<uint8_t *>(&xPsnr[i]));
          }
          msg(NOTICE, " [xY %16" PRIx64 " xU %16" PRIx64 " xV %16" PRIx64 "]", xPsnr[COMPONENT_Y], xPsnr[COMPONENT_Cb], xPsnr[COMPONENT_Cr]);
    
    #if EXTENSION_360_VIDEO
    
          m_ext360.printPerPOCInfo(NOTICE, true);
    
    
        if( printFrameMSE )
        {
          msg( NOTICE, " [Y MSE %6.4lf  U MSE %6.4lf  V MSE %6.4lf]", MSEyuvframe[COMPONENT_Y], MSEyuvframe[COMPONENT_Cb], MSEyuvframe[COMPONENT_Cr] );
        }
    #if WCG_WPSNR
        if (useLumaWPSNR)
        {
          msg(NOTICE, " [WY %6.4lf dB    WU %6.4lf dB    WV %6.4lf dB]", dPSNRWeighted[COMPONENT_Y], dPSNRWeighted[COMPONENT_Cb], dPSNRWeighted[COMPONENT_Cr]);
    
    
          if (m_pcEncLib->getPrintHexPsnr())
          {
            uint64_t xPsnrWeighted[MAX_NUM_COMPONENT];
            for (int i = 0; i < MAX_NUM_COMPONENT; i++)
            {
              copy(reinterpret_cast<uint8_t *>(&dPSNRWeighted[i]),
                   reinterpret_cast<uint8_t *>(&dPSNRWeighted[i]) + sizeof(dPSNRWeighted[i]),
                   reinterpret_cast<uint8_t *>(&xPsnrWeighted[i]));
            }
            msg(NOTICE, " [xWY %16" PRIx64 " xWU %16" PRIx64 " xWV %16" PRIx64 "]", xPsnrWeighted[COMPONENT_Y], xPsnrWeighted[COMPONENT_Cb], xPsnrWeighted[COMPONENT_Cr]);
          }
    
    #if JVET_O0756_CALCULATE_HDRMETRICS
    
        if(calculateHdrMetrics)
        {
          for (int i=0; i<1; i++)
          {
            msg(NOTICE, " [DeltaE%d %6.4lf dB]", (int)m_pcCfg->getWhitePointDeltaE(i), deltaE[i]);
    
            if (m_pcEncLib->getPrintHexPsnr())
            {
              int64_t xdeltaE[MAX_NUM_COMPONENT];
              for (int i = 0; i < 1; i++)
              {
                copy(reinterpret_cast<uint8_t *>(&deltaE[i]),
                     reinterpret_cast<uint8_t *>(&deltaE[i]) + sizeof(deltaE[i]),
                     reinterpret_cast<uint8_t *>(&xdeltaE[i]));
              }
              msg(NOTICE, " [xDeltaE%d %16" PRIx64 "]", (int)m_pcCfg->getWhitePointDeltaE(i), xdeltaE[0]);
            }
    
          }
          for (int i=0; i<1; i++)
          {
            msg(NOTICE, " [PSNRL%d %6.4lf dB]", (int)m_pcCfg->getWhitePointDeltaE(i), psnrL[i]);
    
            
            if (m_pcEncLib->getPrintHexPsnr())
            {
              int64_t xpsnrL[MAX_NUM_COMPONENT];
              for (int i = 0; i < 1; i++)
              {
                copy(reinterpret_cast<uint8_t *>(&psnrL[i]),
                     reinterpret_cast<uint8_t *>(&psnrL[i]) + sizeof(psnrL[i]),
                     reinterpret_cast<uint8_t *>(&xpsnrL[i]));
              }
              msg(NOTICE, " [xPSNRL%d %16" PRIx64 "]", (int)m_pcCfg->getWhitePointDeltaE(i), xpsnrL[0]);
              
            }
    
    #endif
        msg( NOTICE, " [ET %5.0f ]", dEncTime );
    
        // msg( SOME, " [WP %d]", pcSlice->getUseWeightedPrediction());
    
        for( int iRefList = 0; iRefList < 2; iRefList++ )
        {
          msg( NOTICE, " [L%d ", iRefList );
          for( int iRefIndex = 0; iRefIndex < pcSlice->getNumRefIdx( RefPicList( iRefList ) ); iRefIndex++ )
          {
    
    #if RPR_CTC_PRINT
            if( m_pcEncLib->isRPREnabled() )
            {
    
              const std::pair<int, int>& scaleRatio = pcSlice->getScalingRatio( RefPicList( iRefList ), iRefIndex );
    
    
              if( pcSlice->getEnableTMVPFlag() && pcSlice->getColFromL0Flag() == bool( 1 - iRefList ) && pcSlice->getColRefIdx() == iRefIndex )
              {
    
                msg( NOTICE, "%dc(%1.2lfx, %1.2lfx) ", pcSlice->getRefPOC( RefPicList( iRefList ), iRefIndex ), double( scaleRatio.first ) / ( 1 << SCALE_RATIO_BITS ), double( scaleRatio.second ) / ( 1 << SCALE_RATIO_BITS ) );
    
                msg( NOTICE, "%d(%1.2lfx, %1.2lfx) ", pcSlice->getRefPOC( RefPicList( iRefList ), iRefIndex ), double( scaleRatio.first ) / ( 1 << SCALE_RATIO_BITS ), double( scaleRatio.second ) / ( 1 << SCALE_RATIO_BITS ) );
    
            msg( NOTICE, "%d ", pcSlice->getRefPOC( RefPicList( iRefList ), iRefIndex ) );
    
    #if RPR_CTC_PRINT
        if( m_pcEncLib->isRPREnabled() )
        {
          msg( NOTICE, "\nPSNR2: [Y %6.4lf dB    U %6.4lf dB    V %6.4lf dB]", upscaledPSNR[COMPONENT_Y], upscaledPSNR[COMPONENT_Cb], upscaledPSNR[COMPONENT_Cr] );
        }
    #endif
    
      }
      else if( g_verbosity >= INFO )
      {
        std::cout << "\r\t" << pcSlice->getPOC();
        std::cout.flush();
      }
    }
    
    #if JVET_O0756_CALCULATE_HDRMETRICS
    
    void EncGOP::xCalculateHDRMetrics( Picture* pcPic, double deltaE[hdrtoolslib::NB_REF_WHITE], double psnrL[hdrtoolslib::NB_REF_WHITE])
    
      ChromaFormat chFmt =  pcPic->chromaFormat;
    
        m_pcConvertFormat->process(m_ppcFrameOrg[1], m_ppcFrameOrg[0]);
        m_pcConvertFormat->process(m_ppcFrameRec[1], m_ppcFrameRec[0]);
      }
    
      m_pcConvertIQuantize->process(m_ppcFrameOrg[2], m_ppcFrameOrg[1]);
      m_pcConvertIQuantize->process(m_ppcFrameRec[2], m_ppcFrameRec[1]);
    
      m_pcColorTransform->process(m_ppcFrameOrg[3], m_ppcFrameOrg[2]);
      m_pcColorTransform->process(m_ppcFrameRec[3], m_ppcFrameRec[2]);
    
      m_pcTransferFct->forward(m_ppcFrameOrg[4], m_ppcFrameOrg[3]);
      m_pcTransferFct->forward(m_ppcFrameRec[4], m_ppcFrameRec[3]);
    
      // Calculate the Metrics
      m_pcDistortionDeltaE->computeMetric(m_ppcFrameOrg[4], m_ppcFrameRec[4]);
    
      *deltaE = m_pcDistortionDeltaE->getDeltaE();
      *psnrL  = m_pcDistortionDeltaE->getPsnrL();
    
    }
    
    void EncGOP::copyBuftoFrame( Picture* pcPic )
    {
      int cropOffsetLeft   = m_pcCfg->getCropOffsetLeft();
      int cropOffsetTop    = m_pcCfg->getCropOffsetTop();
      int cropOffsetRight  = m_pcCfg->getCropOffsetRight();
      int cropOffsetBottom = m_pcCfg->getCropOffsetBottom();
    
      int height = pcPic->getOrigBuf(COMPONENT_Y).height - cropOffsetLeft + cropOffsetRight;
      int width = pcPic->getOrigBuf(COMPONENT_Y).width - cropOffsetTop + cropOffsetBottom;
    
      ChromaFormat chFmt =  pcPic->chromaFormat;
    
      Pel* pOrg = pcPic->getOrigBuf(COMPONENT_Y).buf;
      Pel* pRec = pcPic->getRecoBuf(COMPONENT_Y).buf;
    
      uint16_t* yOrg = m_ppcFrameOrg[0]->m_ui16Comp[hdrtoolslib::Y_COMP];
      uint16_t* yRec = m_ppcFrameRec[0]->m_ui16Comp[hdrtoolslib::Y_COMP];
      uint16_t* uOrg = m_ppcFrameOrg[0]->m_ui16Comp[hdrtoolslib::Cb_COMP];
      uint16_t* uRec = m_ppcFrameRec[0]->m_ui16Comp[hdrtoolslib::Cb_COMP];
      uint16_t* vOrg = m_ppcFrameOrg[0]->m_ui16Comp[hdrtoolslib::Cr_COMP];
      uint16_t* vRec = m_ppcFrameRec[0]->m_ui16Comp[hdrtoolslib::Cr_COMP];
    
      if(chFmt == CHROMA_444){
        yOrg = m_ppcFrameOrg[1]->m_ui16Comp[hdrtoolslib::Y_COMP];
        yRec = m_ppcFrameRec[1]->m_ui16Comp[hdrtoolslib::Y_COMP];
        uOrg = m_ppcFrameOrg[1]->m_ui16Comp[hdrtoolslib::Cb_COMP];
        uRec = m_ppcFrameRec[1]->m_ui16Comp[hdrtoolslib::Cb_COMP];
        vOrg = m_ppcFrameOrg[1]->m_ui16Comp[hdrtoolslib::Cr_COMP];
        vRec = m_ppcFrameRec[1]->m_ui16Comp[hdrtoolslib::Cr_COMP];
      }
    
      for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
          yOrg[i*width + j] = static_cast<uint16_t>(pOrg[(i + cropOffsetTop) * pcPic->getOrigBuf(COMPONENT_Y).stride + j + cropOffsetLeft]);
          yRec[i*width + j] = static_cast<uint16_t>(pRec[(i + cropOffsetTop) * pcPic->getRecoBuf(COMPONENT_Y).stride + j + cropOffsetLeft]);
    
        cropOffsetLeft >>= 1;
        cropOffsetTop >>= 1;
      }
    
      pOrg = pcPic->getOrigBuf(COMPONENT_Cb).buf;
      pRec = pcPic->getRecoBuf(COMPONENT_Cb).buf;
    
      for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
          uOrg[i*width + j] = static_cast<uint16_t>(pOrg[(i + cropOffsetTop) * pcPic->getOrigBuf(COMPONENT_Cb).stride + j + cropOffsetLeft]);
          uRec[i*width + j] = static_cast<uint16_t>(pRec[(i + cropOffsetTop) * pcPic->getRecoBuf(COMPONENT_Cb).stride + j + cropOffsetLeft]);
    
      pOrg = pcPic->getOrigBuf(COMPONENT_Cr).buf;
      pRec = pcPic->getRecoBuf(COMPONENT_Cr).buf;
    
      for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
          vOrg[i*width + j] = static_cast<uint16_t>(pOrg[(i + cropOffsetTop) * pcPic->getOrigBuf(COMPONENT_Cr).stride + j + cropOffsetLeft]);
          vRec[i*width + j] = static_cast<uint16_t>(pRec[(i + cropOffsetTop) * pcPic->getRecoBuf(COMPONENT_Cr).stride + j + cropOffsetLeft]);
    
    void EncGOP::xCalculateInterlacedAddPSNR( Picture* pcPicOrgFirstField, Picture* pcPicOrgSecondField,
                                              PelUnitBuf cPicRecFirstField, PelUnitBuf cPicRecSecondField,
    
                                              const InputColourSpaceConversion conversion, const bool printFrameMSE, double* PSNR_Y
                                            , bool isEncodeLtRef
    )
    
    {
      const SPS &sps = *pcPicOrgFirstField->cs->sps;
      const ChromaFormat format = sps.getChromaFormatIdc();
      double  dPSNR[MAX_NUM_COMPONENT];
      Picture    *apcPicOrgFields[2] = {pcPicOrgFirstField, pcPicOrgSecondField};
      PelUnitBuf acPicRecFields[2]   = {cPicRecFirstField, cPicRecSecondField};
    #if ENABLE_QPA
      const bool    useWPSNR = m_pcEncLib->getUseWPSNR();
    #endif
      for(int i=0; i<MAX_NUM_COMPONENT; i++)
      {
        dPSNR[i]=0.0;
      }
    
      PelStorage cscd[2 /* first/second field */];
      if (conversion!=IPCOLOURSPACE_UNCHANGED)
      {
        for(uint32_t fieldNum=0; fieldNum<2; fieldNum++)
        {
          PelUnitBuf& reconField= (acPicRecFields[fieldNum]);
          cscd[fieldNum].create( reconField.chromaFormat, Area( Position(), reconField.Y()) );
          VideoIOYuv::ColourSpaceConvert(reconField, cscd[fieldNum], conversion, false);
          acPicRecFields[fieldNum]=cscd[fieldNum];
        }
      }
    
      //===== calculate PSNR =====
      double MSEyuvframe[MAX_NUM_COMPONENT] = {0, 0, 0};
    
      CHECK(!(acPicRecFields[0].chromaFormat==acPicRecFields[1].chromaFormat), "Unspecified error");
      const uint32_t numValidComponents = ::getNumberValidComponents( acPicRecFields[0].chromaFormat );
    
      for (int chan = 0; chan < numValidComponents; chan++)
      {
        const ComponentID ch=ComponentID(chan);
        CHECK(!(acPicRecFields[0].get(ch).width==acPicRecFields[1].get(ch).width), "Unspecified error");
        CHECK(!(acPicRecFields[0].get(ch).height==acPicRecFields[0].get(ch).height), "Unspecified error");
    
        uint64_t uiSSDtemp=0;
        const uint32_t width    = acPicRecFields[0].get(ch).width - (m_pcEncLib->getPad(0) >> ::getComponentScaleX(ch, format));
        const uint32_t height   = acPicRecFields[0].get(ch).height - ((m_pcEncLib->getPad(1) >> 1) >> ::getComponentScaleY(ch, format));
        const uint32_t bitDepth = sps.getBitDepth(toChannelType(ch));
    
        for(uint32_t fieldNum=0; fieldNum<2; fieldNum++)
        {
          CHECK(!(conversion == IPCOLOURSPACE_UNCHANGED), "Unspecified error");
    #if ENABLE_QPA
          uiSSDtemp += xFindDistortionPlane( acPicRecFields[fieldNum].get(ch), apcPicOrgFields[fieldNum]->getOrigBuf().get(ch), useWPSNR ? bitDepth : 0, ::getComponentScaleX(ch, format) );
    #else
          uiSSDtemp += xFindDistortionPlane( acPicRecFields[fieldNum].get(ch), apcPicOrgFields[fieldNum]->getOrigBuf().get(ch), 0 );
    #endif
        }
        const uint32_t maxval = 255 << (bitDepth - 8);
        const uint32_t size   = width * height * 2;
        const double fRefValue = (double)maxval * maxval * size;
        dPSNR[ch]         = uiSSDtemp ? 10.0 * log10(fRefValue / (double)uiSSDtemp) : 999.99;
        MSEyuvframe[ch]   = (double)uiSSDtemp / size;
      }
    
      uint32_t uibits = 0; // the number of bits for the pair is not calculated here - instead the overall total is used elsewhere.
    
      //===== add PSNR =====
    
      m_gcAnalyzeAll_in.addResult (dPSNR, (double)uibits, MSEyuvframe
    
    
      *PSNR_Y = dPSNR[COMPONENT_Y];
    
      msg( DETAILS, "\n                                      Interlaced frame %d: [Y %6.4lf dB    U %6.4lf dB    V %6.4lf dB]", pcPicOrgSecondField->getPOC()/2 , dPSNR[COMPONENT_Y], dPSNR[COMPONENT_Cb], dPSNR[COMPONENT_Cr] );
      if (printFrameMSE)
      {
        msg( DETAILS, " [Y MSE %6.4lf  U MSE %6.4lf  V MSE %6.4lf]", MSEyuvframe[COMPONENT_Y], MSEyuvframe[COMPONENT_Cb], MSEyuvframe[COMPONENT_Cr] );
      }
    
      for(uint32_t fieldNum=0; fieldNum<2; fieldNum++)
      {
        cscd[fieldNum].destroy();
      }
    }
    
    /** Function for deciding the nal_unit_type.
     * \param pocCurr POC of the current picture
     * \param lastIDR  POC of the last IDR picture
     * \param isField  true to indicate field coding
     * \returns the NAL unit type of the picture
     * This function checks the configuration and returns the appropriate nal_unit_type for the picture.
     */
    NalUnitType EncGOP::getNalUnitType(int pocCurr, int lastIDR, bool isField)
    {
      if (pocCurr == 0)
      {
    
    #else
        return NAL_UNIT_CODED_SLICE_IDR_N_LP;
    #endif
    
      if (m_pcCfg->getEfficientFieldIRAPEnabled() && isField && pocCurr == (m_pcCfg->getUseCompositeRef() ? 2: 1))
    
        return NAL_UNIT_CODED_SLICE_TRAIL;
    
      if (m_pcCfg->getDecodingRefreshType() != 3 && (pocCurr - isField) % (m_pcCfg->getIntraPeriod() * (m_pcCfg->getUseCompositeRef() ? 2 : 1)) == 0)
    
      {
        if (m_pcCfg->getDecodingRefreshType() == 1)
        {
          return NAL_UNIT_CODED_SLICE_CRA;
        }
        else if (m_pcCfg->getDecodingRefreshType() == 2)
        {
          return NAL_UNIT_CODED_SLICE_IDR_W_RADL;
        }
      }
      if(m_pocCRA>0)
      {
        if(pocCurr<m_pocCRA)
        {
          // All leading pictures are being marked as TFD pictures here since current encoder uses all
          // reference pictures while encoding leading pictures. An encoder can ensure that a leading
          // picture can be still decodable when random accessing to a CRA/CRANT/BLA/BLANT picture by
          // controlling the reference pictures used for encoding that leading picture. Such a leading
          // picture need not be marked as a TFD picture.
    
          return NAL_UNIT_CODED_SLICE_RASL;
    
          return NAL_UNIT_CODED_SLICE_RADL;
    
      return NAL_UNIT_CODED_SLICE_TRAIL;
    
    }
    
    void EncGOP::xUpdateRasInit(Slice* slice)
    {
      slice->setPendingRasInit( false );
      if ( slice->getPOC() > m_lastRasPoc )
      {
        m_lastRasPoc = MAX_INT;
        slice->setPendingRasInit( true );
      }
      if ( slice->isIRAP() )
      {
        m_lastRasPoc = slice->getPOC();
      }
    }
    
    double EncGOP::xCalculateRVM()
    {
      double dRVM = 0;
    
      if( m_pcCfg->getGOPSize() == 1 && m_pcCfg->getIntraPeriod() != 1 && m_pcCfg->getFramesToBeEncoded() > RVM_VCEGAM10_M * 2 )
      {
        // calculate RVM only for lowdelay configurations
        std::vector<double> vRL , vB;
        size_t N = m_vRVM_RP.size();
        vRL.resize( N );
        vB.resize( N );
    
        int i;
        double dRavg = 0 , dBavg = 0;
        vB[RVM_VCEGAM10_M] = 0;
        for( i = RVM_VCEGAM10_M + 1 ; i < N - RVM_VCEGAM10_M + 1 ; i++ )
        {
          vRL[i] = 0;
          for( int j = i - RVM_VCEGAM10_M ; j <= i + RVM_VCEGAM10_M - 1 ; j++ )
          {
            vRL[i] += m_vRVM_RP[j];
          }
          vRL[i] /= ( 2 * RVM_VCEGAM10_M );
          vB[i] = vB[i-1] + m_vRVM_RP[i] - vRL[i];
          dRavg += m_vRVM_RP[i];
          dBavg += vB[i];
        }
    
        dRavg /= ( N - 2 * RVM_VCEGAM10_M );
        dBavg /= ( N - 2 * RVM_VCEGAM10_M );
    
        double dSigamB = 0;
        for( i = RVM_VCEGAM10_M + 1 ; i < N - RVM_VCEGAM10_M + 1 ; i++ )
        {
          double tmp = vB[i] - dBavg;
          dSigamB += tmp * tmp;
        }
        dSigamB = sqrt( dSigamB / ( N - 2 * RVM_VCEGAM10_M ) );
    
        double f = sqrt( 12.0 * ( RVM_VCEGAM10_M - 1 ) / ( RVM_VCEGAM10_M + 1 ) );
    
        dRVM = dSigamB / dRavg * f;
      }
    
      return( dRVM );
    }
    
    /** Attaches the input bitstream to the stream in the output NAL unit
        Updates rNalu to contain concatenated bitstream. rpcBitstreamRedirect is cleared at the end of this function call.
     *  \param codedSliceData contains the coded slice data (bitstream) to be concatenated to rNalu
     *  \param rNalu          target NAL unit
     */
    void EncGOP::xAttachSliceDataToNalUnit (OutputNALUnit& rNalu, OutputBitstream* codedSliceData)
    {
      // Byte-align
      rNalu.m_Bitstream.writeByteAlignment();   // Slice header byte-alignment
    
      // Perform bitstream concatenation
      if (codedSliceData->getNumberOfWrittenBits() > 0)
      {
        rNalu.m_Bitstream.addSubstream(codedSliceData);
      }
      codedSliceData->clear();
    }
    
    
    
    void EncGOP::arrangeCompositeReference(Slice* pcSlice, PicList& rcListPic, int pocCurr)
    {
      Picture* curPic = NULL;
      PicList::iterator  iterPic = rcListPic.begin();
      const PreCalcValues *pcv = pcSlice->getPPS()->pcv;
      m_bgPOC = pocCurr + 1;
      if (m_picBg->getSpliceFull())
      {
        return;
      }
      while (iterPic != rcListPic.end())
      {
        curPic = *(iterPic++);
        if (curPic->getPOC() == pocCurr)
        {
          break;
        }
      }
    
      if (pcSlice->isIRAP())
    
      {
        return;
      }
    
      int width = pcv->lumaWidth;
      int height = pcv->lumaHeight;
      int stride = curPic->getOrigBuf().get(COMPONENT_Y).stride;
      int cStride = curPic->getOrigBuf().get(COMPONENT_Cb).stride;
      Pel* curLumaAddr = curPic->getOrigBuf().get(COMPONENT_Y).buf;
      Pel* curCbAddr = curPic->getOrigBuf().get(COMPONENT_Cb).buf;
      Pel* curCrAddr = curPic->getOrigBuf().get(COMPONENT_Cr).buf;
      Pel* bgOrgLumaAddr = m_picOrig->getOrigBuf().get(COMPONENT_Y).buf;
      Pel* bgOrgCbAddr = m_picOrig->getOrigBuf().get(COMPONENT_Cb).buf;
      Pel* bgOrgCrAddr = m_picOrig->getOrigBuf().get(COMPONENT_Cr).buf;
      int cuMaxWidth = pcv->maxCUWidth;
      int cuMaxHeight = pcv->maxCUHeight;
      int maxReplace = (pcv->sizeInCtus) / 2;
      maxReplace = maxReplace < 1 ? 1 : maxReplace;
      typedef struct tagCostStr
      {
        double cost;
        int ctuIdx;
      }CostStr;
      CostStr* minCtuCost = new CostStr[maxReplace];
      for (int i = 0; i < maxReplace; i++)
      {
        minCtuCost[i].cost = 1e10;
        minCtuCost[i].ctuIdx = -1;
      }
      int bitIncrementY = pcSlice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) - 8;
      int bitIncrementUV = pcSlice->getSPS()->getBitDepth(CHANNEL_TYPE_CHROMA) - 8;
      for (int y = 0; y < height; y += cuMaxHeight)
      {
        for (int x = 0; x < width; x += cuMaxWidth)
        {
          double lcuDist = 0.0;
          double lcuDistCb = 0.0;
          double lcuDistCr = 0.0;
          int    realPixelCnt = 0;
          double lcuCost = 1e10;
          int largeDist = 0;
    
          for (int tmpy = 0; tmpy < cuMaxHeight; tmpy++)
          {
            if (y + tmpy >= height)
            {
              break;
            }
            for (int tmpx = 0; tmpx < cuMaxWidth; tmpx++)
            {
              if (x + tmpx >= width)
              {
                break;
              }
    
              realPixelCnt++;
              lcuDist += abs(curLumaAddr[(y + tmpy)*stride + x + tmpx] - bgOrgLumaAddr[(y + tmpy)*stride + x + tmpx]);
              if (abs(curLumaAddr[(y + tmpy)*stride + x + tmpx] - bgOrgLumaAddr[(y + tmpy)*stride + x + tmpx]) >(20 << bitIncrementY))
              {
                largeDist++;
              }
    
              if (tmpy % 2 == 0 && tmpx % 2 == 0)
              {
                lcuDistCb += abs(curCbAddr[(y + tmpy) / 2 * cStride + (x + tmpx) / 2] - bgOrgCbAddr[(y + tmpy) / 2 * cStride + (x + tmpx) / 2]);
                lcuDistCr += abs(curCrAddr[(y + tmpy) / 2 * cStride + (x + tmpx) / 2] - bgOrgCrAddr[(y + tmpy) / 2 * cStride + (x + tmpx) / 2]);
              }
            }
          }
    
          //Test the vertical or horizontal edge for background patches candidates
          int yInLCU = y / cuMaxHeight;
          int xInLCU = x / cuMaxWidth;
          int iLCUIdx = yInLCU * pcv->widthInCtus + xInLCU;
          if ((largeDist / (double)realPixelCnt < 0.01 &&lcuDist / realPixelCnt < (3.5 * (1 << bitIncrementY)) && lcuDistCb / realPixelCnt < (0.5 * (1 << bitIncrementUV)) && lcuDistCr / realPixelCnt < (0.5 * (1 << bitIncrementUV)) && m_picBg->getSpliceIdx(iLCUIdx) == 0))
          {
            lcuCost = lcuDist / realPixelCnt + lcuDistCb / realPixelCnt + lcuDistCr / realPixelCnt;
            //obtain the maxReplace smallest cost
            //1) find the largest cost in the maxReplace candidates
            for (int i = 0; i < maxReplace - 1; i++)
            {
              if (minCtuCost[i].cost > minCtuCost[i + 1].cost)
              {
                swap(minCtuCost[i].cost, minCtuCost[i + 1].cost);
                swap(minCtuCost[i].ctuIdx, minCtuCost[i + 1].ctuIdx);
              }
            }
            // 2) compare the current cost with the largest cost
            if (lcuCost < minCtuCost[maxReplace - 1].cost)
            {
              minCtuCost[maxReplace - 1].cost = lcuCost;
              minCtuCost[maxReplace - 1].ctuIdx = iLCUIdx;
            }
          }
        }
      }
    
      // modify QP for background CTU
      {
        for (int i = 0; i < maxReplace; i++)
        {
          if (minCtuCost[i].ctuIdx != -1)
          {
            m_picBg->setSpliceIdx(minCtuCost[i].ctuIdx, pocCurr);
          }
        }
      }
      delete[]minCtuCost;
    }
    
    void EncGOP::updateCompositeReference(Slice* pcSlice, PicList& rcListPic, int pocCurr)
    {
      Picture* curPic = NULL;
      const PreCalcValues *pcv = pcSlice->getPPS()->pcv;
      PicList::iterator  iterPic = rcListPic.begin();
      iterPic = rcListPic.begin();
      while (iterPic != rcListPic.end())
      {
        curPic = *(iterPic++);
        if (curPic->getPOC() == pocCurr)
        {
          break;
        }
      }
      assert(curPic->getPOC() == pocCurr);
    
      int width = pcv->lumaWidth;
      int height = pcv->lumaHeight;
      int stride = curPic->getRecoBuf().get(COMPONENT_Y).stride;
      int cStride = curPic->getRecoBuf().get(COMPONENT_Cb).stride;
    
      Pel* bgLumaAddr = m_picBg->getRecoBuf().get(COMPONENT_Y).buf;
      Pel* bgCbAddr = m_picBg->getRecoBuf().get(COMPONENT_Cb).buf;
      Pel* bgCrAddr = m_picBg->getRecoBuf().get(COMPONENT_Cr).buf;
      Pel* curLumaAddr = curPic->getRecoBuf().get(COMPONENT_Y).buf;
      Pel* curCbAddr = curPic->getRecoBuf().get(COMPONENT_Cb).buf;
      Pel* curCrAddr = curPic->getRecoBuf().get(COMPONENT_Cr).buf;
    
      int maxCuWidth = pcv->maxCUWidth;
      int maxCuHeight = pcv->maxCUHeight;
    
      // Update background reference
    
      if (pcSlice->isIRAP())//(pocCurr == 0)
    
      {
        curPic->extendPicBorder();
        curPic->setBorderExtension(true);
    
        m_picBg->getRecoBuf().copyFrom(curPic->getRecoBuf());
        m_picOrig->getOrigBuf().copyFrom(curPic->getOrigBuf());
      }
      else
      {
        //cout << "update B" << pocCurr << endl;
        for (int y = 0; y < height; y += maxCuHeight)
        {
          for (int x = 0; x < width; x += maxCuWidth)
          {
            if (m_picBg->getSpliceIdx((y / maxCuHeight)*pcv->widthInCtus + x / maxCuWidth) == pocCurr)
            {
              for (int tmpy = 0; tmpy < maxCuHeight; tmpy++)
              {
                if (y + tmpy >= height)
                {
                  break;
                }
                for (int tmpx = 0; tmpx < maxCuWidth; tmpx++)
                {
                  if (x + tmpx >= width)
                  {
                    break;
                  }
                  bgLumaAddr[(y + tmpy)*stride + x + tmpx] = curLumaAddr[(y + tmpy)*stride + x + tmpx];
                  if (tmpy % 2 == 0 && tmpx % 2 == 0)
                  {
                    bgCbAddr[(y + tmpy) / 2 * cStride + (x + tmpx) / 2] = curCbAddr[(y + tmpy) / 2 * cStride + (x + tmpx) / 2];
                    bgCrAddr[(y + tmpy) / 2 * cStride + (x + tmpx) / 2] = curCrAddr[(y + tmpy) / 2 * cStride + (x + tmpx) / 2];
                  }
                }
              }
            }
          }
        }
        m_picBg->setBorderExtension(false);
        m_picBg->extendPicBorder();
        m_picBg->setBorderExtension(true);
    
        curPic->extendPicBorder();
        curPic->setBorderExtension(true);
        m_picOrig->getOrigBuf().copyFrom(curPic->getOrigBuf());
    
        m_picBg->setBorderExtension(false);
        m_picBg->extendPicBorder();
        m_picBg->setBorderExtension(true);
      }
    }
    
    
    void EncGOP::applyDeblockingFilterMetric( Picture* pcPic, uint32_t uiNumSlices )
    {
      PelBuf cPelBuf = pcPic->getRecoBuf().get( COMPONENT_Y );
      Pel* Rec    = cPelBuf.buf;
      const int  stride = cPelBuf.stride;
      const uint32_t picWidth = cPelBuf.width;
      const uint32_t picHeight = cPelBuf.height;
    
      Pel* tempRec = Rec;
      const Slice* pcSlice = pcPic->slices[0];
    
    #if MAX_TB_SIZE_SIGNALLING
      const uint32_t log2maxTB = pcSlice->getSPS()->getLog2MaxTbSize();
    #else
      const uint32_t log2maxTB = MAX_TB_LOG2_SIZEY;
    #endif
      const uint32_t maxTBsize = (1<<log2maxTB);
    
      const uint32_t minBlockArtSize = 8;
      const uint32_t noCol = (picWidth>>log2maxTB);
      const uint32_t noRows = (picHeight>>log2maxTB);
      CHECK(!(noCol > 1), "Unspecified error");
      CHECK(!(noRows > 1), "Unspecified error");
      std::vector<uint64_t> colSAD(noCol,  uint64_t(0));
      std::vector<uint64_t> rowSAD(noRows, uint64_t(0));
      uint32_t colIdx = 0;
      uint32_t rowIdx = 0;
      Pel p0, p1, p2, q0, q1, q2;
    
      int qp = pcSlice->getSliceQp();
      const int bitDepthLuma=pcSlice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA);
      int bitdepthScale = 1 << (bitDepthLuma-8);
      int beta = LoopFilter::getBeta( qp ) * bitdepthScale;
      const int thr2 = (beta>>2);
      const int thr1 = 2*bitdepthScale;
      uint32_t a = 0;
    
      if (maxTBsize > minBlockArtSize)
      {
        // Analyze vertical artifact edges
        for(int c = maxTBsize; c < picWidth; c += maxTBsize)
        {
          for(int r = 0; r < picHeight; r++)