Skip to content

Commit

Permalink
Allow calculating PSNR for Y/U/V components (#3824)
Browse files Browse the repository at this point in the history
* Allow calculating PSNR for Y/U/V components

by requesting this either as a option for all frames in
  SEncParamExt.bPsnrY (and U/V)
or per-frame using
  SSourcePicture.bPsnrY (and U/V)

The resulting data goes into
  SLayerBSInfo.rPsnr
with is a three-element array for the Y/U/V components of the PSNR.

This is disabled by default.
Also removes the ENABLE_PSNR_CALC preprocessor define in favor of the API.

See
  https://www.researchgate.net/publication/383545049_Low-Complexity_Video_PSNR_Measurement_in_Real-Time_Communication_Products
by @YCSun-Meta for a research paper explaining the background.

* move per-pic psnr
  • Loading branch information
fippo authored Jan 6, 2025
1 parent 423eb2c commit 33f7f48
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 37 deletions.
8 changes: 8 additions & 0 deletions codec/api/wels/codec_app_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,9 @@ typedef struct TagEncParamExt {
bool bIsLosslessLink; ///< LTR advanced setting
bool bFixRCOverShoot; ///< fix rate control overshooting
int iIdrBitrateRatio; ///< the target bits of IDR is (idr_bitrate_ratio/100) * average target bit per frame.
bool bPsnrY; ///< get Y PSNR stats for the whole video sequence
bool bPsnrU; ///< get U PSNR stats for the whole video sequence
bool bPsnrV; ///< get V PSNR stats for the whole video sequence
} SEncParamExt;

/**
Expand Down Expand Up @@ -635,6 +638,7 @@ typedef struct {
int iNalCount; ///< count number of NAL coded already
int* pNalLengthInByte; ///< length of NAL size in byte from 0 to iNalCount-1
unsigned char* pBsBuf; ///< buffer of bitstream contained
float rPsnr[3]; ///< PSNR values for Y/U/V
} SLayerBSInfo, *PLayerBSInfo;

/**
Expand All @@ -659,7 +663,11 @@ typedef struct Source_Picture_s {
int iPicWidth; ///< luma picture width in x coordinate
int iPicHeight; ///< luma picture height in y coordinate
long long uiTimeStamp; ///< timestamp of the source picture, unit: millisecond
bool bPsnrY; ///< get Y PSNR for this frame
bool bPsnrU; ///< get U PSNR for this frame
bool bPsnrV; ///< get V PSNR for this frame
} SSourcePicture;

/**
* @brief Structure for bit rate info
*/
Expand Down
5 changes: 0 additions & 5 deletions codec/encoder/core/inc/as264_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@
#endif//ENABLE_FRAME_DUMP
#endif//__UNITTEST__

//#define ENABLE_PSNR_CALC
//#define STAT_OUTPUT
//#define MB_TYPES_CHECK
//
Expand All @@ -88,10 +87,6 @@
//@if !FRAME_INFO_OUTPUT
#if !defined(FRAME_INFO_OUTPUT)

#if defined(ENABLE_PSNR_CALC)
#undef ENABLE_PSNR_CALC
#endif//ENABLE_PSNR_CALC

//#if defined(STAT_OUTPUT)
//#undef STAT_OUTPUT
//#endif//STAT_OUTPUT
Expand Down
6 changes: 6 additions & 0 deletions codec/encoder/core/inc/param_svc.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ typedef struct TagWelsSvcCodingParam: SEncParamExt {
param.bIsLosslessLink = false;
param.bFixRCOverShoot = true;
param.iIdrBitrateRatio = IDR_BITRATE_RATIO * 100;
param.bPsnrY = false;
param.bPsnrU = false;
param.bPsnrV = false;
for (int32_t iLayer = 0; iLayer < MAX_SPATIAL_LAYER_NUM; iLayer++) {
param.sSpatialLayers[iLayer].uiProfileIdc = PRO_UNKNOWN;
param.sSpatialLayers[iLayer].uiLevelIdc = LEVEL_UNKNOWN;
Expand Down Expand Up @@ -345,6 +348,9 @@ typedef struct TagWelsSvcCodingParam: SEncParamExt {
bIsLosslessLink = pCodingParam.bIsLosslessLink;
bFixRCOverShoot = pCodingParam.bFixRCOverShoot;
iIdrBitrateRatio = pCodingParam.iIdrBitrateRatio;
bPsnrY = pCodingParam.bPsnrY;
bPsnrU = pCodingParam.bPsnrU;
bPsnrV = pCodingParam.bPsnrV;
if (iUsageType == SCREEN_CONTENT_REAL_TIME && !bIsLosslessLink && bEnableLongTermReference) {
bEnableLongTermReference = false;
}
Expand Down
79 changes: 47 additions & 32 deletions codec/encoder/core/src/encoder_ext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2186,7 +2186,7 @@ void StatOverallEncodingExt (sWelsEncCtx* pCtx) {

}
}
#endif
#endif //#if defined(STAT_OUTPUT)


int32_t GetMultipleThreadIdc (SLogContext* pLogCtx, SWelsSvcCodingParam* pCodingParam, int16_t& iSliceNum,
Expand Down Expand Up @@ -3445,9 +3445,7 @@ int32_t WelsEncoderEncodeExt (sWelsEncCtx* pCtx, SFrameBSInfo* pFbi, const SSour
SLayerBSInfo* pLayerBsInfo = &pFbi->sLayerInfo[0];
SWelsSvcCodingParam* pSvcParam = pCtx->pSvcParam;
SSpatialPicIndex* pSpatialIndexMap = &pCtx->sSpatialIndexMap[0];
#if defined(ENABLE_FRAME_DUMP) || defined(ENABLE_PSNR_CALC)
SPicture* fsnr = NULL;
#endif//ENABLE_FRAME_DUMP || ENABLE_PSNR_CALC
SPicture* pEncPic = NULL; // to be decided later
#if defined(MT_DEBUG)
int32_t iDidList[MAX_DEPENDENCY_LAYER] = {0};
Expand All @@ -3469,9 +3467,7 @@ int32_t WelsEncoderEncodeExt (sWelsEncCtx* pCtx, SFrameBSInfo* pFbi, const SSour
int32_t iCurTid = 0;
bool bAvcBased = false;
SLogContext* pLogCtx = & (pCtx->sLogCtx);
#if defined(ENABLE_PSNR_CALC)
float fSnrY = .0f, fSnrU = .0f, fSnrV = .0f;
#endif//ENABLE_PSNR_CALC

#if defined(_DEBUG)
int32_t i = 0, j = 0, k = 0;
Expand Down Expand Up @@ -3624,9 +3620,7 @@ int32_t WelsEncoderEncodeExt (sWelsEncCtx* pCtx, SFrameBSInfo* pFbi, const SSour
pCtx->eNalPriority = eNalRefIdc;

pCtx->pDecPic = pCtx->ppRefPicListExt[iCurDid]->pNextBuffer;
#if defined(ENABLE_FRAME_DUMP) || defined(ENABLE_PSNR_CALC)
fsnr = pCtx->pDecPic;
#endif//#if defined(ENABLE_FRAME_DUMP) || defined(ENABLE_PSNR_CALC)
pCtx->pDecPic->iPictureType = pCtx->eSliceType;
pCtx->pDecPic->iFramePoc = pParamInternal->iPOC;

Expand Down Expand Up @@ -3921,26 +3915,30 @@ int32_t WelsEncoderEncodeExt (sWelsEncCtx* pCtx, SFrameBSInfo* pFbi, const SSour
}
#endif//ENABLE_FRAME_DUMP

#if defined(ENABLE_PSNR_CALC)
fSnrY = WelsCalcPsnr (fsnr->pData[0],
fsnr->iLineSize[0],
pEncPic->pData[0],
pEncPic->iLineSize[0],
iCurWidth,
iCurHeight);
fSnrU = WelsCalcPsnr (fsnr->pData[1],
fsnr->iLineSize[1],
pEncPic->pData[1],
pEncPic->iLineSize[1],
(iCurWidth >> 1),
(iCurHeight >> 1));
fSnrV = WelsCalcPsnr (fsnr->pData[2],
fsnr->iLineSize[2],
pEncPic->pData[2],
pEncPic->iLineSize[2],
(iCurWidth >> 1),
(iCurHeight >> 1));
#endif//ENABLE_PSNR_CALC
if (fsnr && (pSvcParam->bPsnrY || pSrcPic->bPsnrY)) {
fSnrY = WelsCalcPsnr (fsnr->pData[0],
fsnr->iLineSize[0],
pEncPic->pData[0],
pEncPic->iLineSize[0],
iCurWidth,
iCurHeight);
}
if (fsnr && (pSvcParam->bPsnrU || pSrcPic->bPsnrU)) {
fSnrU = WelsCalcPsnr (fsnr->pData[1],
fsnr->iLineSize[1],
pEncPic->pData[1],
pEncPic->iLineSize[1],
(iCurWidth >> 1),
(iCurHeight >> 1));
}
if (fsnr && (pSvcParam->bPsnrV || pSrcPic->bPsnrV)) {
fSnrV = WelsCalcPsnr (fsnr->pData[2],
fsnr->iLineSize[2],
pEncPic->pData[2],
pEncPic->iLineSize[2],
(iCurWidth >> 1),
(iCurHeight >> 1));
}

#if defined(LAYER_INFO_OUTPUT)
fprintf (stderr, "%2s %5d: %-5d %2s T%1d D%1d Q%-2d QP%3d Y%2.2f U%2.2f V%2.2f %8d bits\n",
Expand All @@ -3958,15 +3956,32 @@ int32_t WelsEncoderEncodeExt (sWelsEncCtx* pCtx, SFrameBSInfo* pFbi, const SSour
(iLayerSize << 3));
#endif//LAYER_INFO_OUTPUT

pLayerBsInfo->rPsnr[0] = NAN;
pLayerBsInfo->rPsnr[1] = NAN;
pLayerBsInfo->rPsnr[2] = NAN;
if (pSrcPic->bPsnrY) {
pLayerBsInfo->rPsnr[0] = fSnrY;
}
if (pSrcPic->bPsnrU) {
pLayerBsInfo->rPsnr[1] = fSnrU;
}
if (pSrcPic->bPsnrV) {
pLayerBsInfo->rPsnr[2] = fSnrV;
}

#if defined(STAT_OUTPUT)

#if defined(ENABLE_PSNR_CALC)
{
pCtx->sStatData[iCurDid][0].sQualityStat.rYPsnr[pCtx->eSliceType] += fSnrY;
pCtx->sStatData[iCurDid][0].sQualityStat.rUPsnr[pCtx->eSliceType] += fSnrU;
pCtx->sStatData[iCurDid][0].sQualityStat.rVPsnr[pCtx->eSliceType] += fSnrV;
if (pSvcParam->bPsnrY) {
pCtx->sStatData[iCurDid][0].sQualityStat.rYPsnr[pCtx->eSliceType] += fSnrY;
}
if (pSvcParam->bPsnrU) {
pCtx->sStatData[iCurDid][0].sQualityStat.rUPsnr[pCtx->eSliceType] += fSnrU;
}
if (pSvcParam->bPsnrV) {
pCtx->sStatData[iCurDid][0].sQualityStat.rVPsnr[pCtx->eSliceType] += fSnrV;
}
}
#endif//ENABLE_PSNR_CALC

#if defined(MB_TYPES_CHECK) //091025, frame output
if (pCtx->eSliceType == P_SLICE) {
Expand Down

0 comments on commit 33f7f48

Please sign in to comment.