在HEVC官方参考软件HM中,计算PSNR的代码如下,当然还有很多工具计算PSNR。
现在提供一种很方便的工具,只需给出原始YUV文件和解码或重构后的YUV文件就可以计算出其PSNR。软件下载地址为:点击打开链接
为了验证其正确性,本人测试了一下,结果如下图所示。
下图是HM得出的结果:
下图是该软件测出的结果:
结果一致。
Void TEncGOP::xCalculateAddPSNR( TComPic* pcPic, TComPicYuv* pcPicD, const AccessUnit& accessUnit, Double dEncTime ) { Int x, y; UInt64 uiSSDY = 0; UInt64 uiSSDU = 0; UInt64 uiSSDV = 0; Double dYPSNR = 0.0; Double dUPSNR = 0.0; Double dVPSNR = 0.0; //===== calculate PSNR ===== Pel* pOrg = pcPic ->getPicYuvOrg()->getLumaAddr(); Pel* pRec = pcPicD->getLumaAddr(); Int iStride = pcPicD->getStride(); Int iWidth; Int iHeight; iWidth = pcPicD->getWidth () - m_pcEncTop->getPad(0); iHeight = pcPicD->getHeight() - m_pcEncTop->getPad(1); Int iSize = iWidth*iHeight; for( y = 0; y < iHeight; y++ ) { for( x = 0; x < iWidth; x++ ) { Int iDiff = (Int)( pOrg[x] - pRec[x] ); uiSSDY += iDiff * iDiff; } pOrg += iStride; pRec += iStride; } iHeight >>= 1; iWidth >>= 1; iStride >>= 1; pOrg = pcPic ->getPicYuvOrg()->getCbAddr(); pRec = pcPicD->getCbAddr(); for( y = 0; y < iHeight; y++ ) { for( x = 0; x < iWidth; x++ ) { Int iDiff = (Int)( pOrg[x] - pRec[x] ); uiSSDU += iDiff * iDiff; } pOrg += iStride; pRec += iStride; } pOrg = pcPic ->getPicYuvOrg()->getCrAddr(); pRec = pcPicD->getCrAddr(); for( y = 0; y < iHeight; y++ ) { for( x = 0; x < iWidth; x++ ) { Int iDiff = (Int)( pOrg[x] - pRec[x] ); uiSSDV += iDiff * iDiff; } pOrg += iStride; pRec += iStride; } Int maxvalY = 255 << (g_bitDepthY-8); Int maxvalC = 255 << (g_bitDepthC-8); Double fRefValueY = (Double) maxvalY * maxvalY * iSize; Double fRefValueC = (Double) maxvalC * maxvalC * iSize / 4.0; dYPSNR = ( uiSSDY ? 10.0 * log10( fRefValueY / (Double)uiSSDY ) : 99.99 ); dUPSNR = ( uiSSDU ? 10.0 * log10( fRefValueC / (Double)uiSSDU ) : 99.99 ); dVPSNR = ( uiSSDV ? 10.0 * log10( fRefValueC / (Double)uiSSDV ) : 99.99 ); /* calculate the size of the access unit, excluding: * - any AnnexB contributions (start_code_prefix, zero_byte, etc.,) * - SEI NAL units */ UInt numRBSPBytes = 0; for (AccessUnit::const_iterator it = accessUnit.begin(); it != accessUnit.end(); it++) { UInt numRBSPBytes_nal = UInt((*it)->m_nalUnitData.str().size()); #if VERBOSE_RATE printf("*** %6s numBytesInNALunit: %u\n", nalUnitTypeToString((*it)->m_nalUnitType), numRBSPBytes_nal); #endif if ((*it)->m_nalUnitType != NAL_UNIT_PREFIX_SEI && (*it)->m_nalUnitType != NAL_UNIT_SUFFIX_SEI) { numRBSPBytes += numRBSPBytes_nal; } } UInt uibits = numRBSPBytes * 8; m_vRVM_RP.push_back( uibits ); //===== add PSNR ===== m_gcAnalyzeAll.addResult (dYPSNR, dUPSNR, dVPSNR, (Double)uibits); TComSlice* pcSlice = pcPic->getSlice(0); if (pcSlice->isIntra()) { m_gcAnalyzeI.addResult (dYPSNR, dUPSNR, dVPSNR, (Double)uibits); } if (pcSlice->isInterP()) { m_gcAnalyzeP.addResult (dYPSNR, dUPSNR, dVPSNR, (Double)uibits); } if (pcSlice->isInterB()) { m_gcAnalyzeB.addResult (dYPSNR, dUPSNR, dVPSNR, (Double)uibits); } Char c = (pcSlice->isIntra() ? 'I' : pcSlice->isInterP() ? 'P' : 'B'); if (!pcSlice->isReferenced()) c += 32; #if ADAPTIVE_QP_SELECTION printf("POC %4d TId: %1d ( %c-SLICE, nQP %d QP %d ) %10d bits", pcSlice->getPOC(), pcSlice->getTLayer(), c, pcSlice->getSliceQpBase(), pcSlice->getSliceQp(), uibits ); #else printf("POC %4d TId: %1d ( %c-SLICE, QP %d ) %10d bits", pcSlice->getPOC()-pcSlice->getLastIDR(), pcSlice->getTLayer(), c, pcSlice->getSliceQp(), uibits ); #endif printf(" [Y %6.4lf dB U %6.4lf dB V %6.4lf dB]", dYPSNR, dUPSNR, dVPSNR ); printf(" [ET %5.0f ]", dEncTime ); for (Int iRefList = 0; iRefList < 2; iRefList++) { printf(" [L%d ", iRefList); for (Int iRefIndex = 0; iRefIndex < pcSlice->getNumRefIdx(RefPicList(iRefList)); iRefIndex++) { printf ("%d ", pcSlice->getRefPOC(RefPicList(iRefList), iRefIndex)-pcSlice->getLastIDR()); } printf("]"); } }