欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

360Lib:CPP-PSNR

程序员文章站 2024-03-24 11:07:46
...

CPP-PSNR是360Lib采纳的一种360视频的客观质量评估标准,通过投影到CPP格式计算PSNR。

TCPPPSNRMetric

下面来看360Lib中定义的CPP-PSNR类TCPPPSNRMetric,其中定义了重要函数xCalculateCPPPSNR,用于计算CPP-PSNR。

class TCPPPSNRMetric        //CPP PSNR
{
private:
  Bool          m_bCPPPSNREnabled;  
  Double        m_dCPPPSNR[3];

  CPos2D*       m_pCart2D;
  IPos2D*       m_fpTable;
  Int           m_iSphNumPoints;
  Int           m_cppWidth;   //CPP宽度
  Int           m_cppHeight;     //CPP高度

  Int           m_outputBitDepth[MAX_NUM_CHANNEL_TYPE];         ///< bit-depth of output file 输出文件的bit深度
  Int           m_referenceBitDepth[MAX_NUM_CHANNEL_TYPE];      ///< bit-depth of reference file 参考文件的bit深度
  ChromaFormat  m_chromaFormatIDC;  //颜色格式
#if SVIDEO_CPP_FIX
  InputGeoParam m_refGeoParam;
  InputGeoParam m_outGeoParam;
#else
  InputGeoParam m_cppGeoParam; 
#endif
  SVideoInfo    m_cppCodingVideoInfo;
  SVideoInfo    m_cppRefVideoInfo;
  SVideoInfo    m_cppVideoInfo;

  Void          setDefaultFramePackingParam(SVideoInfo& sVideoInfo);        //设置默认参数
  Void          fillSourceSVideoInfo(SVideoInfo& sVidInfo, Int inputWidth, Int inputHeight);    //根据投影格式填充源视频信息

  TGeometry     *m_pcOutputGeomtry;
  TGeometry     *m_pcReferenceGeomtry;
  TGeometry     *m_pcOutputCPPGeomtry;
  TGeometry     *m_pcRefCPPGeomtry;

public:
  TCPPPSNRMetric();
  virtual ~TCPPPSNRMetric();
  Bool    getCPPPSNREnabled()  { return m_bCPPPSNREnabled; }        //获取m_bCPPPSNREnabled
  Void    setCPPPSNREnabledFlag(Bool bEnabledFlag)  { m_bCPPPSNREnabled = bEnabledFlag; }       //设置m_bCPPPSNREnabled
  Void    setOutputBitDepth(Int iOutputBitDepth[MAX_NUM_CHANNEL_TYPE]);     //设置输出bit深度
  Void    setReferenceBitDepth(Int iReferenceBitDepth[MAX_NUM_CHANNEL_TYPE]);   //设置参考图形bit深度
  Void    setCPPWidth(Int iCPPWidth);       //设置CPP宽度
  Void    setCPPHeight(Int iCPPheight);     //设置CPP高度
  Void    setChromaFormatIDC(ChromaFormat iChromaFormatIDC);        //设置颜色格式
#if SVIDEO_CPP_FIX
  Void    setGeoParam(InputGeoParam iCPPGeoParam);
#else
  Void    setCPPGeoParam(InputGeoParam iCPPGeoParam);
#endif
  Void    setCPPVideoInfo(SVideoInfo iCppCodingVdideoInfo, SVideoInfo iCppRefVdideoInfo);       //设置CPP视频的编码视频和参考视频信息
  Double* getCPPPSNR() {return m_dCPPPSNR;}     //获取CPP-PSNR
  //Void    sphSampoints(Char* cSphDataFile);
  Void    sphToCart(CPos2D*, CPos3D*);      //2D to 3D
  Void    xCalculateCPPPSNR( TComPicYuv* pcOrgPicYuv, TComPicYuv* pcPicD );     //计算CPP-PSNR
  Void    initCPPPSNR(InputGeoParam inputGeoParam, Int cppWidth, Int cppHeight, SVideoInfo codingvideoInfo, SVideoInfo referenceVideoInfo);     //初始化CPP-PSNR
};

xCalculateCPPPSNR

下面来看xCalculateCPPPSNR函数,可以分为两部分:
1.将参考图像和输出图像转换为了CPP格式。
2.计算CPP格式下的PSNR即为CPP-PSNR。
CPP下存在无效图像区域,因此在计算PSNR前先计算了有效区域坐标。

//计算CPP-PSNR
Void TCPPPSNRMetric::xCalculateCPPPSNR( TComPicYuv* pcOrgPicYuv, TComPicYuv* pcPicD)
{
  Int iBitDepthForPSNRCalc[MAX_NUM_CHANNEL_TYPE];       //计算时的bit深度
  Int iReferenceBitShift[MAX_NUM_CHANNEL_TYPE];         //参考图像bit偏移
  Int iOutputBitShift[MAX_NUM_CHANNEL_TYPE];        //输出bit偏移

  TComPicYuv *TPicYUVRefCPP;        //参考图像
  TComPicYuv *TPicYUVOutCPP;        //输出图像

  //计算时的bit深度为输出bit深度和输入bit深度的最大值
  iBitDepthForPSNRCalc[CHANNEL_TYPE_LUMA] = std::max(m_outputBitDepth[CHANNEL_TYPE_LUMA], m_referenceBitDepth[CHANNEL_TYPE_LUMA]);
  iBitDepthForPSNRCalc[CHANNEL_TYPE_CHROMA] = std::max(m_outputBitDepth[CHANNEL_TYPE_CHROMA], m_referenceBitDepth[CHANNEL_TYPE_CHROMA]);
  //参考图像bit偏移 = 计算时的bit深度 - 参考图像bit深度
  iReferenceBitShift[CHANNEL_TYPE_LUMA] = iBitDepthForPSNRCalc[CHANNEL_TYPE_LUMA] - m_referenceBitDepth[CHANNEL_TYPE_LUMA];
  iReferenceBitShift[CHANNEL_TYPE_CHROMA] = iBitDepthForPSNRCalc[CHANNEL_TYPE_CHROMA] - m_referenceBitDepth[CHANNEL_TYPE_CHROMA];
  //输出图像bit偏移 = 计算时的bit深度 - 输出图像bit深度
  iOutputBitShift[CHANNEL_TYPE_LUMA] = iBitDepthForPSNRCalc[CHANNEL_TYPE_LUMA] - m_outputBitDepth[CHANNEL_TYPE_LUMA];
  iOutputBitShift[CHANNEL_TYPE_CHROMA] = iBitDepthForPSNRCalc[CHANNEL_TYPE_CHROMA] - m_outputBitDepth[CHANNEL_TYPE_CHROMA];

  memset(m_dCPPPSNR, 0, sizeof(Double)*3);
  Double SCPPDspsnr[3]={0, 0 ,0};

  // Convert Output and Ref to CPP_Projection
  //初始化CPP格式参考图像
  TPicYUVRefCPP = new TComPicYuv;
  TPicYUVRefCPP->createWithoutCUInfo  ( m_cppWidth, m_cppHeight, m_chromaFormatIDC, true );

  //初始化CPP格式输出图像
  TPicYUVOutCPP = new TComPicYuv;
  TPicYUVOutCPP->createWithoutCUInfo  ( m_cppWidth, m_cppHeight, m_chromaFormatIDC, true );

  // Converting Reference to CPP
  //将参考图像转换为CPP格式
  if ((m_pcReferenceGeomtry->getSVideoInfo()->geoType == SVIDEO_OCTAHEDRON || m_pcReferenceGeomtry->getSVideoInfo()->geoType == SVIDEO_ICOSAHEDRON) && m_pcReferenceGeomtry->getSVideoInfo()->iCompactFPStructure)
  {
    m_pcReferenceGeomtry->compactFramePackConvertYuv(pcOrgPicYuv);
  }
  else
  {
    m_pcReferenceGeomtry->convertYuv(pcOrgPicYuv);
  }
  m_pcReferenceGeomtry->geoConvert(m_pcRefCPPGeomtry);      //投影格式转换
  m_pcRefCPPGeomtry->framePack(TPicYUVRefCPP);      

  // Converting Output to CPP
  //将输出图像转换为CPP格式
  if ((m_pcOutputGeomtry->getSVideoInfo()->geoType == SVIDEO_OCTAHEDRON || m_pcOutputGeomtry->getSVideoInfo()->geoType == SVIDEO_ICOSAHEDRON) && m_pcOutputGeomtry->getSVideoInfo()->iCompactFPStructure)
  {
    m_pcOutputGeomtry->compactFramePackConvertYuv(pcPicD);
  }
  else
  {
    m_pcOutputGeomtry->convertYuv(pcPicD);
  }
  m_pcOutputGeomtry->geoConvert(m_pcOutputCPPGeomtry);  //投影格式转换
  m_pcOutputCPPGeomtry->framePack(TPicYUVOutCPP);

 for(Int chan=0; chan<pcPicD->getNumberValidComponents(); chan++)
  {
    //初始化亮度、色度的信息
    const ComponentID ch=ComponentID(chan);
    const Pel*  pOrg       = TPicYUVOutCPP->getAddr(ch);        //原始图像首地址
    const Int   iOrgStride = TPicYUVOutCPP->getStride(ch);  //原始图像跨度
    const Pel*  pRec       = TPicYUVRefCPP->getAddr(ch);        //重构图像首地址
    const Int   iRecStride = TPicYUVRefCPP->getStride(ch);      //重构图像跨度
    const Int   iWidth     = TPicYUVRefCPP->getWidth (ch);  //图像宽度
    const Int   iHeight    = TPicYUVRefCPP->getHeight(ch);  //图像高度

    Int   iSize            = 0;
    double fPhi, fLambda;
    double fIdxX, fIdxY;
    double fLamdaX, fLamdaY;

    //计算CPP格式下有效图像区域的PSNR
    for(Int y=0;y<iHeight;y++)
    {
      for(Int x=0;x<iWidth;x++)
      {
        fLamdaX = ((double)x / (iWidth)) * (2 * S_PI) - S_PI;
        fLamdaY = ((double)y / (iHeight)) * S_PI - (S_PI_2);

        fPhi = 3 * sasin(fLamdaY / S_PI);
        fLambda = fLamdaX / (2 * scos(2 * fPhi / 3) - 1);

        fLamdaX = (fLambda + S_PI) / 2 / S_PI * (iWidth);
        fLamdaY = (fPhi + (S_PI / 2)) / S_PI *  (iHeight);

        fIdxX = (int)((fLamdaX < 0) ? fLamdaX - 0.5 : fLamdaX + 0.5);
        fIdxY = (int)((fLamdaY < 0) ? fLamdaY - 0.5 : fLamdaY + 0.5);

        if(fIdxY >= 0 && fIdxX >= 0 && fIdxX < iWidth && fIdxY < iHeight)       //判断是否越界
        {
#if SVIDEO_CPP_FIX
          Intermediate_Int iDifflp = (Intermediate_Int)((pOrg[x] << iOutputBitShift[toChannelType(ch)]) - (pRec[x] << iReferenceBitShift[toChannelType(ch)]));
#else
          Intermediate_Int iDifflp  = (Intermediate_Int)((pOrg[x]<<iReferenceBitShift[toChannelType(ch)]) - (pRec[x]<<iOutputBitShift[toChannelType(ch)]) );
#endif
          SCPPDspsnr[chan]         += iDifflp * iDifflp;        //平方差累加
          iSize++;      //记录累加点的数量
        }
      }
      pOrg += iOrgStride;
      pRec += iRecStride;
    }
    SCPPDspsnr[chan] /= iSize;      //求MSE
  }

  for (Int ch_indx = 0; ch_indx < pcPicD->getNumberValidComponents(); ch_indx++)
  {
    const ComponentID ch=ComponentID(ch_indx);
    const Int maxval = 255<<(iBitDepthForPSNRCalc[toChannelType(ch)]-8) ;

    Double fReflpsnr = maxval*maxval;
    //求CPP-PSNR
    m_dCPPPSNR[ch_indx] = ( SCPPDspsnr[ch_indx] ? 10.0 * log10( fReflpsnr / (Double)SCPPDspsnr[ch_indx] ) : 999.99 );   
  }

  //清理内存
  if(TPicYUVRefCPP)
  {
    TPicYUVRefCPP->destroy();
    delete TPicYUVRefCPP;
    TPicYUVRefCPP = NULL;
  }
  if(TPicYUVOutCPP)
  {
    TPicYUVOutCPP->destroy();
    delete TPicYUVOutCPP;
    TPicYUVOutCPP = NULL;
  }
}

从代码来看,CPP-PSNR计算过程中,计算坐标fIdxX和fIdxY时存在舍入误差,引入了失真。在格式转换中,还使用了插值滤波器,也会引入失真。

ERP到CPP格式的转换

在360Lib中,读入的360视频都是以2D形式表示,在xCalculateCPPPSNR中,CPP格式由原始视频2D格式转换得到。以参考图像为例,这里我只研究输入为ERP的情况,那执行的代码为:

  //投影格式转换
  m_pcReferenceGeomtry->convertYuv(pcOrgPicYuv);
  m_pcReferenceGeomtry->geoConvert(m_pcRefCPPGeomtry);      
  m_pcRefCPPGeomtry->framePack(TPicYUVRefCPP);      

转换过程分为3步:
1.扩展到360Lib 2D平面:通过convertYuv,将ERP图像填充扩展到360Lib 2D坐标平面上。
2.几何信息转换:通过geoConvert进行几何信息转换。
3.YUV信息转换:通过framePack对YUV信息的转换。

实际格式转换由geoConvert实现,格式转换中需要进行插值,因此会引入失真。

相关标签: 360视频