Newer
Older
{
ParameterSetMap<APS> *apsMap = m_pcEncLib->getApsMap();
APS* aps = apsMap->getPS((apsId << NUM_APS_TYPE_LEN) + ALF_APS);
bool writeAPS = aps && apsMap->getChangedFlag((apsId << NUM_APS_TYPE_LEN) + ALF_APS);
if (!aps && pcSlice->getAlfAPSs() && pcSlice->getAlfAPSs()[apsId])
{
writeAPS = true;
aps = pcSlice->getAlfAPSs()[apsId]; // use asp from slice header
*apsMap->allocatePS(apsId) = *aps; //allocate and cpy
m_pcALF->setApsIdStart( apsId );
}
#if JVET_N0278_FIXES
actualTotalBits += xWriteAPS( accessUnit, aps, m_pcEncLib->getLayerId() );
#else
actualTotalBits += xWriteAPS(accessUnit, aps);
apsMap->clearChangedFlag((apsId << NUM_APS_TYPE_LEN) + ALF_APS);
CHECK(aps != pcSlice->getAlfAPSs()[apsId], "Wrong APS pointer in compressGOP");
}
}
}

Karsten Suehring
committed
// reset presence of BP SEI indication
m_bufferingPeriodSEIPresentInAU = false;
// create prefix SEI associated with a picture
xCreatePerPictureSEIMessages(iGOPid, leadingSeiMessages, nestedSeiMessages, pcSlice);
// pcSlice is currently slice 0.
std::size_t binCountsInNalUnits = 0; // For implementation of cabac_zero_word stuffing (section 7.4.3.10)
std::size_t numBytesInVclNalUnits = 0; // For implementation of cabac_zero_word stuffing (section 7.4.3.10)
for(uint32_t sliceSegmentStartCtuTsAddr = 0, sliceSegmentIdxCount = 0; sliceSegmentStartCtuTsAddr < numberOfCtusInFrame; sliceSegmentIdxCount++, sliceSegmentStartCtuTsAddr = pcSlice->getSliceCurEndCtuTsAddr())
{
pcSlice = pcPic->slices[sliceSegmentIdxCount];
if(sliceSegmentIdxCount > 0 && pcSlice->getSliceType()!= I_SLICE)
{
pcSlice->checkColRefIdx(sliceSegmentIdxCount, pcPic);
}
m_pcSliceEncoder->setSliceSegmentIdx(sliceSegmentIdxCount);
pcSlice->setRPL0(pcPic->slices[0]->getRPL0());
pcSlice->setRPL1(pcPic->slices[0]->getRPL1());
pcSlice->setRPL0idx(pcPic->slices[0]->getRPL0idx());
pcSlice->setRPL1idx(pcPic->slices[0]->getRPL1idx());

Karsten Suehring
committed
for ( uint32_t ui = 0 ; ui < numSubstreams; ui++ )
{
substreamsOut[ui].clear();
}
/* start slice NALunit */
#if JVET_N0278_FIXES
OutputNALUnit nalu( pcSlice->getNalUnitType(), m_pcEncLib->getLayerId(), pcSlice->getTLayer() );
#else

Karsten Suehring
committed
OutputNALUnit nalu( pcSlice->getNalUnitType(), pcSlice->getTLayer() );

Karsten Suehring
committed
m_HLSWriter->setBitstream( &nalu.m_Bitstream );
pcSlice->setNoIncorrectPicOutputFlag(false);

Karsten Suehring
committed
if (pcSlice->isIRAP())
{
if (pcSlice->getNalUnitType() >= NAL_UNIT_CODED_SLICE_IDR_W_RADL && pcSlice->getNalUnitType() <= NAL_UNIT_CODED_SLICE_IDR_N_LP)

Karsten Suehring
committed
{
pcSlice->setNoIncorrectPicOutputFlag(true);

Karsten Suehring
committed
}
//the inference for NoOutputPriorPicsFlag
// KJS: This cannot happen at the encoder
if (!m_bFirst && (pcSlice->isIRAP() || pcSlice->getNalUnitType() >= NAL_UNIT_CODED_SLICE_GDR) && pcSlice->getNoIncorrectPicOutputFlag())
{
if (pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_CRA || pcSlice->getNalUnitType() >= NAL_UNIT_CODED_SLICE_GDR)

Karsten Suehring
committed
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
{
pcSlice->setNoOutputPriorPicsFlag(true);
}
}
}
tmpBitsBeforeWriting = m_HLSWriter->getNumberOfWrittenBits();
m_HLSWriter->codeSliceHeader( pcSlice );
actualHeadBits += ( m_HLSWriter->getNumberOfWrittenBits() - tmpBitsBeforeWriting );
pcSlice->setFinalized(true);
pcSlice->clearSubstreamSizes( );
{
uint32_t numBinsCoded = 0;
m_pcSliceEncoder->encodeSlice(pcPic, &(substreamsOut[0]), numBinsCoded);
binCountsInNalUnits+=numBinsCoded;
}
{
// Construct the final bitstream by concatenating substreams.
// The final bitstream is either nalu.m_Bitstream or pcBitstreamRedirect;
// Complete the slice header info.
m_HLSWriter->setBitstream( &nalu.m_Bitstream );
m_HLSWriter->codeTilesWPPEntryPoint( pcSlice );
// Append substreams...
OutputBitstream *pcOut = pcBitstreamRedirect;
const int numSubstreamsToCode = pcSlice->getNumberOfSubstreamSizes()+1;
for ( uint32_t ui = 0 ; ui < numSubstreamsToCode; ui++ )
{
pcOut->addSubstream(&(substreamsOut[ui]));

Karsten Suehring
committed
}
}
// If current NALU is the first NALU of slice (containing slice header) and more NALUs exist (due to multiple dependent slices) then buffer it.
// If current NALU is the last NALU of slice and a NALU was buffered, then (a) Write current NALU (b) Update an write buffered NALU at approproate location in NALU list.
bool bNALUAlignedWrittenToList = false; // used to ensure current NALU is not written more than once to the NALU list.
xAttachSliceDataToNalUnit(nalu, pcBitstreamRedirect);
accessUnit.push_back(new NALUnitEBSP(nalu));
actualTotalBits += uint32_t(accessUnit.back()->m_nalUnitData.str().size()) * 8;
numBytesInVclNalUnits += (std::size_t)(accessUnit.back()->m_nalUnitData.str().size());
bNALUAlignedWrittenToList = true;
if (!bNALUAlignedWrittenToList)
{
nalu.m_Bitstream.writeAlignZero();
accessUnit.push_back(new NALUnitEBSP(nalu));
}
if( ( m_pcCfg->getPictureTimingSEIEnabled() || m_pcCfg->getDecodingUnitInfoSEIEnabled() ) &&
( ( pcSlice->getSPS()->getHrdParameters()->getNalHrdParametersPresentFlag() )
|| ( pcSlice->getSPS()->getHrdParameters()->getVclHrdParametersPresentFlag() ) ) &&
( pcSlice->getSPS()->getHrdParameters()->getDecodingUnitHrdParamsPresentFlag() ) )

Karsten Suehring
committed
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
{
uint32_t numNalus = 0;
uint32_t numRBSPBytes = 0;
for (AccessUnit::const_iterator it = accessUnit.begin(); it != accessUnit.end(); it++)
{
numRBSPBytes += uint32_t((*it)->m_nalUnitData.str().size());
numNalus ++;
}
duData.push_back(DUData());
duData.back().accumBitsDU = ( numRBSPBytes << 3 );
duData.back().accumNalsDU = numNalus;
}
} // end iteration over slices
// cabac_zero_words processing
cabac_zero_word_padding(pcSlice, pcPic, binCountsInNalUnits, numBytesInVclNalUnits, accessUnit.back()->m_nalUnitData, m_pcCfg->getCabacZeroWordPaddingEnabled());
//-- For time output for each slice
auto elapsed = std::chrono::steady_clock::now() - beforeTime;
auto encTime = std::chrono::duration_cast<std::chrono::seconds>( elapsed ).count();
std::string digestStr;
if (m_pcCfg->getDecodedPictureHashSEIType()!=HASHTYPE_NONE)
{
SEIDecodedPictureHash *decodedPictureHashSei = new SEIDecodedPictureHash();
PelUnitBuf recoBuf = pcPic->cs->getRecoBuf();
m_seiEncoder.initDecodedPictureHashSEI(decodedPictureHashSei, recoBuf, digestStr, pcSlice->getSPS()->getBitDepths());
trailingSeiMessages.push_back(decodedPictureHashSei);
}
m_pcCfg->setEncodedFlag(iGOPid, true);
double PSNR_Y;
xCalculateAddPSNRs(isField, isTff, iGOPid, pcPic, accessUnit, rcListPic, encTime, snr_conversion, printFrameMSE, &PSNR_Y, isEncodeLtRef );

Karsten Suehring
committed
#if HEVC_SEI

Karsten Suehring
committed
// Only produce the Green Metadata SEI message with the last picture.
if( m_pcCfg->getSEIGreenMetadataInfoSEIEnable() && pcSlice->getPOC() == ( m_pcCfg->getFramesToBeEncoded() - 1 ) )
{
SEIGreenMetadataInfo *seiGreenMetadataInfo = new SEIGreenMetadataInfo;
m_seiEncoder.initSEIGreenMetadataInfo(seiGreenMetadataInfo, (uint32_t)(PSNR_Y * 100 + 0.5));
trailingSeiMessages.push_back(seiGreenMetadataInfo);
}
#endif

Karsten Suehring
committed
xWriteTrailingSEIMessages(trailingSeiMessages, accessUnit, pcSlice->getTLayer(), pcSlice->getSPS());
printHash(m_pcCfg->getDecodedPictureHashSEIType(), digestStr);
if ( m_pcCfg->getUseRateCtrl() )
{
double avgQP = m_pcRateCtrl->getRCPic()->calAverageQP();
double avgLambda = m_pcRateCtrl->getRCPic()->calAverageLambda();
if ( avgLambda < 0.0 )
{
avgLambda = lambda;
}
m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda, pcSlice->isIRAP());

Karsten Suehring
committed
m_pcRateCtrl->getRCPic()->addToPictureLsit( m_pcRateCtrl->getPicList() );
m_pcRateCtrl->getRCSeq()->updateAfterPic( actualTotalBits );

Karsten Suehring
committed
{
m_pcRateCtrl->getRCGOP()->updateAfterPicture( actualTotalBits );
}
else // for intra picture, the estimated bits are used to update the current status in the GOP
{
m_pcRateCtrl->getRCGOP()->updateAfterPicture( estimatedBits );
}
#if U0132_TARGET_BITS_SATURATION
if (m_pcRateCtrl->getCpbSaturationEnabled())
{
m_pcRateCtrl->updateCpbState(actualTotalBits);
msg( NOTICE, " [CPB %6d bits]", m_pcRateCtrl->getCpbState() );
}
#endif
}
xCreateFrameFieldInfoSEI( leadingSeiMessages, pcSlice, isField );

Karsten Suehring
committed
xCreatePictureTimingSEI( m_pcCfg->getEfficientFieldIRAPEnabled() ? effFieldIRAPMap.GetIRAPGOPid() : 0, leadingSeiMessages, nestedSeiMessages, duInfoSeiMessages, pcSlice, isField, duData );
#if HEVC_SEI
if( m_pcCfg->getScalableNestingSEIEnabled() )

Karsten Suehring
committed
{
xCreateScalableNestingSEI( leadingSeiMessages, nestedSeiMessages );
}
#endif

Karsten Suehring
committed
xWriteLeadingSEIMessages( leadingSeiMessages, duInfoSeiMessages, accessUnit, pcSlice->getTLayer(), pcSlice->getSPS(), duData );
xWriteDuSEIMessages( duInfoSeiMessages, accessUnit, pcSlice->getTLayer(), pcSlice->getSPS(), duData );
m_AUWriterIf->outputAU( accessUnit );
msg( NOTICE, "\n" );
fflush( stdout );
}
DTRACE_UPDATE( g_trace_ctx, ( std::make_pair( "final", 0 ) ) );
pcPic->reconstructed = true;
m_bFirst = false;
m_iNumPicCoded++;
if (!(m_pcCfg->getUseCompositeRef() && isEncodeLtRef))
{
for( int i = pcSlice->getTLayer() ; i < pcSlice->getSPS()->getMaxTLayers() ; i ++ )
{
m_totalCoded[i]++;
}
}

Karsten Suehring
committed
/* logging: insert a newline at end of picture period */
if (m_pcCfg->getEfficientFieldIRAPEnabled())
{
iGOPid=effFieldIRAPMap.restoreGOPid(iGOPid);
}
pcPic->destroyTempBuffers();
pcPic->cs->destroyCoeffs();
pcPic->cs->releaseIntermediateData();
} // iGOPid-loop
delete pcBitstreamRedirect;
#if JVET_N0278_FIXES
CHECK( m_iNumPicCoded > 1, "Unspecified error" );
#else

Karsten Suehring
committed
CHECK(!( (m_iNumPicCoded == iNumPicRcvd) ), "Unspecified error");

Karsten Suehring
committed
}
void EncGOP::printOutSummary( uint32_t uiNumAllPicCoded, bool isField, const bool printMSEBasedSNR, const bool printSequenceMSE, const bool printHexPsnr, const bool printRprPSNR, const BitDepths &bitDepths )

Karsten Suehring
committed
{
#if ENABLE_QPA
const bool useWPSNR = m_pcEncLib->getUseWPSNR();
#endif
#if WCG_WPSNR
const bool useLumaWPSNR = m_pcEncLib->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcCfg->getReshaper() && m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ);

Karsten Suehring
committed
#endif
if( m_pcCfg->getDecodeBitstream(0).empty() && m_pcCfg->getDecodeBitstream(1).empty() && !m_pcCfg->useFastForwardToPOC() )
{
CHECK( !( uiNumAllPicCoded == m_gcAnalyzeAll.getNumPic() ), "Unspecified error" );
}
//--CFG_KDY
const int rateMultiplier=(isField?2:1);
m_gcAnalyzeAll.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier / (double)m_pcCfg->getTemporalSubsampleRatio());
m_gcAnalyzeI.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier / (double)m_pcCfg->getTemporalSubsampleRatio());
m_gcAnalyzeP.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier / (double)m_pcCfg->getTemporalSubsampleRatio());
m_gcAnalyzeB.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier / (double)m_pcCfg->getTemporalSubsampleRatio());
#if WCG_WPSNR
if (useLumaWPSNR)
{
m_gcAnalyzeWPSNR.setFrmRate(m_pcCfg->getFrameRate()*rateMultiplier / (double)m_pcCfg->getTemporalSubsampleRatio());
}
#endif

Karsten Suehring
committed
const ChromaFormat chFmt = m_pcCfg->getChromaFormatIdc();
//-- all
msg( INFO, "\n" );
msg( DETAILS,"\nSUMMARY --------------------------------------------------------\n" );
Dominik Mehlem
committed
#if JVET_O0756_CALCULATE_HDRMETRICS
const bool calculateHdrMetrics = m_pcEncLib->getCalcluateHdrMetrics();
#endif

Karsten Suehring
committed
#if ENABLE_QPA
Vadim Seregin
committed
m_gcAnalyzeAll.printOut( 'a', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, printRprPSNR, bitDepths, useWPSNR
Dominik Mehlem
committed
#if JVET_O0756_CALCULATE_HDRMETRICS
, calculateHdrMetrics
#endif
);

Karsten Suehring
committed
#else
Dominik Mehlem
committed
m_gcAnalyzeAll.printOut('a', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths
#if JVET_O0756_CALCULATE_HDRMETRICS
, calculateHdrMetrics
#endif
);

Karsten Suehring
committed
#endif
msg( DETAILS, "\n\nI Slices--------------------------------------------------------\n" );
m_gcAnalyzeI.printOut( 'i', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, printRprPSNR, bitDepths );
msg( DETAILS, "\n\nP Slices--------------------------------------------------------\n" );
m_gcAnalyzeP.printOut( 'p', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, printRprPSNR, bitDepths );
msg( DETAILS, "\n\nB Slices--------------------------------------------------------\n" );
m_gcAnalyzeB.printOut( 'b', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, printRprPSNR, bitDepths );

Karsten Suehring
committed
#if WCG_WPSNR
if (useLumaWPSNR)
{
msg(DETAILS, "\nWPSNR SUMMARY --------------------------------------------------------\n");
m_gcAnalyzeWPSNR.printOut( 'w', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, printRprPSNR, bitDepths, useLumaWPSNR );

Karsten Suehring
committed
}
#endif
if (!m_pcCfg->getSummaryOutFilename().empty())
{
m_gcAnalyzeAll.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryOutFilename());

Karsten Suehring
committed
}
if (!m_pcCfg->getSummaryPicFilenameBase().empty())
{
m_gcAnalyzeI.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryPicFilenameBase()+"I.txt");
m_gcAnalyzeP.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryPicFilenameBase()+"P.txt");
m_gcAnalyzeB.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryPicFilenameBase()+"B.txt");

Karsten Suehring
committed
}
#if WCG_WPSNR
if (!m_pcCfg->getSummaryOutFilename().empty() && useLumaWPSNR)
{
m_gcAnalyzeWPSNR.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryOutFilename());

Karsten Suehring
committed
}
#endif
if(isField)
{
//-- interlaced summary
m_gcAnalyzeAll_in.setFrmRate( m_pcCfg->getFrameRate() / (double)m_pcCfg->getTemporalSubsampleRatio());
m_gcAnalyzeAll_in.setBits(m_gcAnalyzeAll.getBits());
// prior to the above statement, the interlace analyser does not contain the correct total number of bits.
msg( DETAILS,"\n\nSUMMARY INTERLACED ---------------------------------------------\n" );
#if ENABLE_QPA
m_gcAnalyzeAll_in.printOut( 'a', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, printRprPSNR, bitDepths, useWPSNR );

Karsten Suehring
committed
#else
m_gcAnalyzeAll_in.printOut('a', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths);

Karsten Suehring
committed
#endif
if (!m_pcCfg->getSummaryOutFilename().empty())
{
m_gcAnalyzeAll_in.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryOutFilename());

Karsten Suehring
committed
#if WCG_WPSNR
if (useLumaWPSNR)
{
m_gcAnalyzeWPSNR.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryOutFilename());

Karsten Suehring
committed
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
}
#endif
}
}
msg( DETAILS,"\nRVM: %.3lf\n", xCalculateRVM() );
}
#if W0038_DB_OPT
uint64_t EncGOP::preLoopFilterPicAndCalcDist( Picture* pcPic )
{
CodingStructure& cs = *pcPic->cs;
m_pcLoopFilter->loopFilterPic( cs );
const CPelUnitBuf picOrg = pcPic->getRecoBuf();
const CPelUnitBuf picRec = cs.getRecoBuf();
uint64_t uiDist = 0;
for( uint32_t comp = 0; comp < (uint32_t)picRec.bufs.size(); comp++)
{
const ComponentID compID = ComponentID(comp);
const uint32_t rshift = 2 * DISTORTION_PRECISION_ADJUSTMENT(cs.sps->getBitDepth(toChannelType(compID)));
#if ENABLE_QPA
CHECK( rshift >= 8, "shifts greater than 7 are not supported." );
#endif
uiDist += xFindDistortionPlane( picOrg.get(compID), picRec.get(compID), rshift );
}
return uiDist;
}
#endif
// ====================================================================================================================
// Protected member functions
// ====================================================================================================================
void EncGOP::xInitGOP( int iPOCLast, int iNumPicRcvd, bool isField
, bool isEncodeLtRef
)

Karsten Suehring
committed
{
CHECK(!( iNumPicRcvd > 0 ), "Unspecified error");
// Exception for the first frames
if ((isField && (iPOCLast == 0 || iPOCLast == 1)) || (!isField && (iPOCLast == 0)) || isEncodeLtRef)

Karsten Suehring
committed
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
{
m_iGopSize = 1;
}
else
{
m_iGopSize = m_pcCfg->getGOPSize();
}
CHECK(!(m_iGopSize > 0), "Unspecified error");
return;
}
void EncGOP::xGetBuffer( PicList& rcListPic,
std::list<PelUnitBuf*>& rcListPicYuvRecOut,
int iNumPicRcvd,
int iTimeOffset,
Picture*& rpcPic,
int pocCurr,
bool isField )
{
int i;
// Rec. output
std::list<PelUnitBuf*>::iterator iterPicYuvRec = rcListPicYuvRecOut.end();
if (isField && pocCurr > 1 && m_iGopSize!=1)
{
iTimeOffset--;
}
int multipleFactor = m_pcCfg->getUseCompositeRef() ? 2 : 1;
for (i = 0; i < (iNumPicRcvd * multipleFactor - iTimeOffset + 1); i += multipleFactor)

Karsten Suehring
committed
{
iterPicYuvRec--;
}
// Current pic.
PicList::iterator iterPic = rcListPic.begin();
while (iterPic != rcListPic.end())
{
rpcPic = *(iterPic);
if( rpcPic->getPOC() == pocCurr && rpcPic->layerId == m_pcEncLib->getLayerId() )

Karsten Suehring
committed
if (rpcPic->getPOC() == pocCurr)

Karsten Suehring
committed
{
break;
}
iterPic++;
}
CHECK(!(rpcPic != NULL), "Unspecified error");
CHECK(!(rpcPic->getPOC() == pocCurr), "Unspecified error");
(**iterPicYuvRec) = rpcPic->getRecoBuf();
return;
}
#if ENABLE_QPA
#ifndef BETA
#define BETA 0.5 // value between 0.0 and 1; use 0.0 to obtain traditional PSNR

Karsten Suehring
committed
#endif
static inline double calcWeightedSquaredError(const CPelBuf& org, const CPelBuf& rec,
double &sumAct, const uint32_t bitDepth,
const uint32_t imageWidth, const uint32_t imageHeight,
const uint32_t offsetX, const uint32_t offsetY,
int blockWidth, int blockHeight)

Karsten Suehring
committed
{
const int O = org.stride;
const int R = rec.stride;
const Pel *o = org.bufAt(offsetX, offsetY);
const Pel *r = rec.bufAt(offsetX, offsetY);
const int yAct = offsetY > 0 ? 0 : 1;
const int xAct = offsetX > 0 ? 0 : 1;
if (offsetY + (uint32_t)blockHeight > imageHeight) blockHeight = imageHeight - offsetY;
if (offsetX + (uint32_t)blockWidth > imageWidth ) blockWidth = imageWidth - offsetX;
const int hAct = offsetY + (uint32_t)blockHeight < imageHeight ? blockHeight : blockHeight - 1;
const int wAct = offsetX + (uint32_t)blockWidth < imageWidth ? blockWidth : blockWidth - 1;
uint64_t ssErr = 0; // sum of squared diffs
uint64_t saAct = 0; // sum of abs. activity

Karsten Suehring
committed
double msAct;
int x, y;
// calculate image differences and activity
for (y = 0; y < blockHeight; y++) // error
{
for (x = 0; x < blockWidth; x++)
{
const int64_t iDiff = (int64_t)o[y*O + x] - (int64_t)r[y*R + x];

Karsten Suehring
committed
ssErr += uint64_t(iDiff * iDiff);
}
}
if (wAct <= xAct || hAct <= yAct) return (double)ssErr;
for (y = yAct; y < hAct; y++) // activity
{
for (x = xAct; x < wAct; x++)
{
const int f = 12 * (int)o[y*O + x] - 2 * ((int)o[y*O + x-1] + (int)o[y*O + x+1] + (int)o[(y-1)*O + x] + (int)o[(y+1)*O + x])
- (int)o[(y-1)*O + x-1] - (int)o[(y-1)*O + x+1] - (int)o[(y+1)*O + x-1] - (int)o[(y+1)*O + x+1];
saAct += abs(f);

Karsten Suehring
committed
}
}
// calculate weight (mean squared activity)
msAct = (double)saAct / (double(wAct - xAct) * double(hAct - yAct));
// lower limit, accounts for high-pass gain
if (msAct < double(1 << (bitDepth - 4))) msAct = double(1 << (bitDepth - 4));

Karsten Suehring
committed
msAct *= msAct; // because ssErr is squared
sumAct += msAct; // includes high-pass gain
// calculate activity weighted error square
return (double)ssErr * pow(msAct, -1.0 * BETA);
}
#endif // ENABLE_QPA
uint64_t EncGOP::xFindDistortionPlane(const CPelBuf& pic0, const CPelBuf& pic1, const uint32_t rshift
#if ENABLE_QPA
Yang Wang
committed
, const uint32_t chromaShiftHor /*= 0*/, const uint32_t chromaShiftVer /*= 0*/

Karsten Suehring
committed
#endif

Karsten Suehring
committed
{
uint64_t uiTotalDiff;
const Pel* pSrc0 = pic0.bufAt(0, 0);
const Pel* pSrc1 = pic1.bufAt(0, 0);
CHECK(pic0.width != pic1.width , "Unspecified error");
CHECK(pic0.height != pic1.height, "Unspecified error");
if( rshift > 0 )
{
#if ENABLE_QPA
const uint32_t BD = rshift; // image bit-depth
if (BD >= 8)
{
const uint32_t W = pic0.width; // image width
const uint32_t H = pic0.height; // image height
const double R = double(W * H) / (1920.0 * 1080.0);
Yang Wang
committed
const uint32_t B = Clip3<uint32_t>(0, 128 >> chromaShiftVer, 4 * uint32_t(16.0 * sqrt(R) + 0.5)); // WPSNR block size in integer multiple of 4 (for SIMD, = 64 at full-HD)

Karsten Suehring
committed
uint32_t x, y;
if (B < 4) // image is too small to use WPSNR, resort to traditional PSNR
{
uiTotalDiff = 0;
for (y = 0; y < H; y++)
{
for (x = 0; x < W; x++)
{
const int64_t iDiff = (int64_t)pSrc0[x] - (int64_t)pSrc1[x];

Karsten Suehring
committed
uiTotalDiff += uint64_t(iDiff * iDiff);
}
pSrc0 += pic0.stride;
pSrc1 += pic1.stride;
}
return uiTotalDiff;
}
double wmse = 0.0, sumAct = 0.0; // compute activity normalized SNR value

Christian Helmrich
committed

Karsten Suehring
committed
for (y = 0; y < H; y += B)
{
for (x = 0; x < W; x += B)
{
wmse += calcWeightedSquaredError(pic1, pic0,
sumAct, BD,
W, H,
x, y,
B, B);

Karsten Suehring
committed
}
}
// integer weighted distortion
Yang Wang
committed
sumAct = 16.0 * sqrt ((3840.0 * 2160.0) / double((W << chromaShiftHor) * (H << chromaShiftVer))) * double(1 << BD);

Karsten Suehring
committed
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
return (wmse <= 0.0) ? 0 : uint64_t(wmse * pow(sumAct, BETA) + 0.5);
}
#endif // ENABLE_QPA
uiTotalDiff = 0;
for (int y = 0; y < pic0.height; y++)
{
for (int x = 0; x < pic0.width; x++)
{
Intermediate_Int iTemp = pSrc0[x] - pSrc1[x];
uiTotalDiff += uint64_t((iTemp * iTemp) >> rshift);
}
pSrc0 += pic0.stride;
pSrc1 += pic1.stride;
}
}
else
{
uiTotalDiff = 0;
for (int y = 0; y < pic0.height; y++)
{
for (int x = 0; x < pic0.width; x++)
{
Intermediate_Int iTemp = pSrc0[x] - pSrc1[x];
uiTotalDiff += uint64_t(iTemp * iTemp);
}
pSrc0 += pic0.stride;
pSrc1 += pic1.stride;
}
}
return uiTotalDiff;
}
#if WCG_WPSNR
double EncGOP::xFindDistortionPlaneWPSNR(const CPelBuf& pic0, const CPelBuf& pic1, const uint32_t rshift, const CPelBuf& picLuma0,

Karsten Suehring
committed
ComponentID compID, const ChromaFormat chfmt )
{
const bool useLumaWPSNR = m_pcEncLib->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcCfg->getReshaper() && m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ);

Karsten Suehring
committed
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
if (!useLumaWPSNR)
{
return 0;
}
double uiTotalDiffWPSNR;
const Pel* pSrc0 = pic0.bufAt(0, 0);
const Pel* pSrc1 = pic1.bufAt(0, 0);
const Pel* pSrcLuma = picLuma0.bufAt(0, 0);
CHECK(pic0.width != pic1.width , "Unspecified error");
CHECK(pic0.height != pic1.height, "Unspecified error");
if( rshift > 0 )
{
uiTotalDiffWPSNR = 0;
for (int y = 0; y < pic0.height; y++)
{
for (int x = 0; x < pic0.width; x++)
{
Intermediate_Int iTemp = pSrc0[x] - pSrc1[x];
double dW = m_pcEncLib->getRdCost()->getWPSNRLumaLevelWeight(pSrcLuma[(x << getComponentScaleX(compID, chfmt))]);
uiTotalDiffWPSNR += ((dW * (double)iTemp * (double)iTemp)) * (double)(1 >> rshift);
}
pSrc0 += pic0.stride;
pSrc1 += pic1.stride;
pSrcLuma += picLuma0.stride << getComponentScaleY(compID, chfmt);
}
}
else
{
uiTotalDiffWPSNR = 0;
for (int y = 0; y < pic0.height; y++)
{
for (int x = 0; x < pic0.width; x++)
{
Intermediate_Int iTemp = pSrc0[x] - pSrc1[x];
double dW = m_pcEncLib->getRdCost()->getWPSNRLumaLevelWeight(pSrcLuma[x << getComponentScaleX(compID, chfmt)]);
uiTotalDiffWPSNR += dW * (double)iTemp * (double)iTemp;
}
pSrc0 += pic0.stride;
pSrc1 += pic1.stride;
pSrcLuma += picLuma0.stride << getComponentScaleY(compID, chfmt);
}
}
return uiTotalDiffWPSNR;
}
#endif
void EncGOP::xCalculateAddPSNRs( const bool isField, const bool isFieldTopFieldFirst, const int iGOPid, Picture* pcPic, const AccessUnit&accessUnit, PicList &rcListPic, const int64_t dEncTime, const InputColourSpaceConversion snr_conversion, const bool printFrameMSE, double* PSNR_Y
, bool isEncodeLtRef
)

Karsten Suehring
committed
{
xCalculateAddPSNR(pcPic, pcPic->getRecoBuf(), accessUnit, (double)dEncTime, snr_conversion, printFrameMSE, PSNR_Y
, isEncodeLtRef
);

Karsten Suehring
committed
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
//In case of field coding, compute the interlaced PSNR for both fields
if(isField)
{
bool bothFieldsAreEncoded = false;
int correspondingFieldPOC = pcPic->getPOC();
int currentPicGOPPoc = m_pcCfg->getGOPEntry(iGOPid).m_POC;
if(pcPic->getPOC() == 0)
{
// particular case for POC 0 and 1.
// If they are not encoded first and separately from other pictures, we need to change this
// POC 0 is always encoded first then POC 1 is encoded
bothFieldsAreEncoded = false;
}
else if(pcPic->getPOC() == 1)
{
// if we are at POC 1, POC 0 has been encoded for sure
correspondingFieldPOC = 0;
bothFieldsAreEncoded = true;
}
else
{
if(pcPic->getPOC()%2 == 1)
{
correspondingFieldPOC -= 1; // all odd POC are associated with the preceding even POC (e.g poc 1 is associated to poc 0)
currentPicGOPPoc -= 1;
}
else
{
correspondingFieldPOC += 1; // all even POC are associated with the following odd POC (e.g poc 0 is associated to poc 1)
currentPicGOPPoc += 1;
}
for(int i = 0; i < m_iGopSize; i ++)
{
if(m_pcCfg->getGOPEntry(i).m_POC == currentPicGOPPoc)
{
bothFieldsAreEncoded = m_pcCfg->getGOPEntry(i).m_isEncoded;
break;
}
}
}
if(bothFieldsAreEncoded)
{
//get complementary top field
PicList::iterator iterPic = rcListPic.begin();
while ((*iterPic)->getPOC() != correspondingFieldPOC)
{
iterPic ++;
}
Picture* correspondingFieldPic = *(iterPic);
if ((pcPic->topField && isFieldTopFieldFirst) || (!pcPic->topField && !isFieldTopFieldFirst))

Karsten Suehring
committed
{
xCalculateInterlacedAddPSNR(pcPic, correspondingFieldPic, pcPic->getRecoBuf(), correspondingFieldPic->getRecoBuf(), snr_conversion, printFrameMSE, PSNR_Y
, isEncodeLtRef
);

Karsten Suehring
committed
}
else
{
xCalculateInterlacedAddPSNR(correspondingFieldPic, pcPic, correspondingFieldPic->getRecoBuf(), pcPic->getRecoBuf(), snr_conversion, printFrameMSE, PSNR_Y
, isEncodeLtRef
);

Karsten Suehring
committed
}
}
}
}
void EncGOP::xCalculateAddPSNR(Picture* pcPic, PelUnitBuf cPicD, const AccessUnit& accessUnit, double dEncTime, const InputColourSpaceConversion conversion, const bool printFrameMSE, double* PSNR_Y
, bool isEncodeLtRef
)

Karsten Suehring
committed
{
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();
Rickard Sjöberg
committed
#if JVET_O0549_ENCODER_ONLY_FILTER
const CPelUnitBuf& org = (sps.getUseReshaper() || m_pcCfg->getGopBasedTemporalFilterEnabled()) ? pcPic->getTrueOrigBuf() : pcPic->getOrigBuf();
#else
const CPelUnitBuf& org = sps.getUseReshaper() ? pcPic->getTrueOrigBuf() : pcPic->getOrigBuf();
Rickard Sjöberg
committed
#endif

Karsten Suehring
committed
#if ENABLE_QPA
const bool useWPSNR = m_pcEncLib->getUseWPSNR();
#endif
double dPSNR[MAX_NUM_COMPONENT];
#if WCG_WPSNR
const bool useLumaWPSNR = m_pcEncLib->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcCfg->getReshaper() && m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ);

Karsten Suehring
committed
double dPSNRWeighted[MAX_NUM_COMPONENT];
double MSEyuvframeWeighted[MAX_NUM_COMPONENT];
#endif
double upscaledPSNR[MAX_NUM_COMPONENT];

Karsten Suehring
committed
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
upscaledPSNR[i] = 0.0;

Karsten Suehring
committed
}
#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;

Karsten Suehring
committed
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];
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 ) );
// 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 );
}

Karsten Suehring
committed
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
Yang Wang
committed
const uint64_t uiSSDtemp = xFindDistortionPlane(recPB, orgPB, useWPSNR ? bitDepth : 0, ::getComponentScaleX(compID, format), ::getComponentScaleY(compID, format));

Karsten Suehring
committed
#else
const uint64_t uiSSDtemp = xFindDistortionPlane(recPB, orgPB, 0);
#endif

Christian Helmrich
committed
const uint32_t maxval = 255 << (bitDepth - 8);

Karsten Suehring
committed
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);

Karsten Suehring
committed
if (useLumaWPSNR)
{
dPSNRWeighted[comp] = uiSSDtempWeighted ? 10.0 * log10(fRefValue / (double)uiSSDtempWeighted) : 999.99;
MSEyuvframeWeighted[comp] = (double)uiSSDtempWeighted / size;
}
#endif
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
Yang Wang
committed
const uint64_t upscaledSSD = xFindDistortionPlane( upscaledRec.get( compID ), upscaledOrg, useWPSNR ? bitDepth : 0, ::getComponentScaleX( compID, format ), ::getComponentScaleY( 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;
}

Karsten Suehring
committed
}
#if EXTENSION_360_VIDEO
m_ext360.calculatePSNRs(pcPic);
#endif
#if JVET_O0756_CALCULATE_HDRMETRICS
const bool calculateHdrMetrics = m_pcEncLib->getCalcluateHdrMetrics();
if (calculateHdrMetrics)
{
auto beforeTime = std::chrono::steady_clock::now();
xCalculateHDRMetrics(pcPic, deltaE, psnrL);
auto elapsed = std::chrono::steady_clock::now() - beforeTime;
m_metricTime += elapsed;
}
#endif

Karsten Suehring
committed
/* 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;
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)

Karsten Suehring
committed
{
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
, upscaledPSNR
, isEncodeLtRef
);

Karsten Suehring
committed
#if EXTENSION_360_VIDEO
m_ext360.addResult(m_gcAnalyzeAll);
#endif
#if JVET_O0756_CALCULATE_HDRMETRICS
if (calculateHdrMetrics)
{
m_gcAnalyzeAll.addHDRMetricsResult(deltaE, psnrL);

Karsten Suehring
committed
#endif
if (pcSlice->isIntra())
{
m_gcAnalyzeI.addResult(dPSNR, (double)uibits, MSEyuvframe
, upscaledPSNR
, isEncodeLtRef
);

Karsten Suehring
committed
*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);

Karsten Suehring
committed
#endif
}
if (pcSlice->isInterP())
{
m_gcAnalyzeP.addResult(dPSNR, (double)uibits, MSEyuvframe
, upscaledPSNR
, isEncodeLtRef
);

Karsten Suehring
committed
*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);

Karsten Suehring
committed
#endif
}
if (pcSlice->isInterB())
{
m_gcAnalyzeB.addResult(dPSNR, (double)uibits, MSEyuvframe
, upscaledPSNR
, isEncodeLtRef
);

Karsten Suehring
committed
*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);

Karsten Suehring
committed
#endif
}
#if WCG_WPSNR
if (useLumaWPSNR)
{
m_gcAnalyzeWPSNR.addResult( dPSNRWeighted, (double)uibits, MSEyuvframeWeighted, upscaledPSNR, isEncodeLtRef );

Karsten Suehring
committed
}
#endif
char c = (pcSlice->isIntra() ? 'I' : pcSlice->isInterP() ? 'P' : 'B');
if (! pcPic->referenced)
{
c += 32;
}
if (m_pcCfg->getDependentRAPIndicationSEIEnabled() && pcSlice->isDRAP()) c = 'D';

Karsten Suehring
committed
if( g_verbosity >= NOTICE )
{
#if JVET_N0278_FIXES
msg( NOTICE, "POC %4d LId: %2d TId: %1d ( %c-SLICE, QP %d ) %10d bits",
pcSlice->getPOC(),
pcSlice->getPic()->layerId,
#else

Karsten Suehring
committed
msg( NOTICE, "POC %4d TId: %1d ( %c-SLICE, QP %d ) %10d bits",

Karsten Suehring
committed
pcSlice->getTLayer(),
c,
pcSlice->getSliceQp(),