• VVC 预测值写入TXT文件(xCheckModeSplit()函数未写完)



    VTM1.0代码阅读:CodingStructure类主要函数_矛盾统一的博客-CSDN博客

    C++文件输入输出,看这一篇就够了_Jasmine-Lily的博客-CSDN博客_c++文件输入输出

    C++ STL标准库: std::list使用介绍、用法详解_超级大洋葱806的博客-CSDN博客_std::list 取值

    1.CTU的预测值写入TXT

    area写法

    1.得到当前CU的区域信息

    (1)这里的类是UnitArea类,存储的是区域所有分量的位置信息

    const UnitArea &area = cu;

    cu包含了很多通用数据,所以直接用对应类建立变量,然后等于CU即可将数据传输过去。

    (2)这里的类是CompArea类,存储的是区域选中分量的位置信息

    const CompArea &area = cu.blocks[COMPONENT_Y];

    area所属类的不同,后面得到getPredBuf()也不同

    2.将预测信息存入

    方法1:(代码位置放在在estIntraPredLumaQT函数最后面,1532行)

    1. //测试存储预测缓存
    2. PelBuf piPredtest = cs.getPredBuf (area);
    3. AreaBuf& recYpoint = piPredtest;
    4. std::ofstream out("C:\\Users\\qjjt\\Desktop\\输出.txt");
    5. for(int i = 0;i < widthtest;i++)
    6. {
    7. for(int j = 0;j < heighttest;j++)
    8. {
    9. out<<"预测值 " << "宽=" << i << "高=" << j << " " << recYpoint.at(i,j) << std::endl;
    10. }
    11. }

    这个有问题,这只能得到当前测试CU的每个像素点的预测值,而且当前测试CU并不是最终划分方式确定的CU,会得到很多无效的预测信息。

    方法2:(代码位置放在compressCtu函数中的xCompressCU后面)

    这里的BestCs里保存的是最优划分,直接可用。所以就选择放在这里了

    基本参数(放在compressCtu函数里partitioner后面)

    1. //坐标
    2. int posx = area.lumaPos().x;
    3. int posy = area.lumaPos().y;
    4. //CTU的宽高
    5. const uint32_t widthtest = partitioner.currArea().lwidth();
    6. const uint32_t heighttest = partitioner.currArea().lheight();
    7. //亮度分量的区域信息
    8. const CompArea &_area = area.blocks[COMPONENT_Y];

    实现代码(代码位置放在compressCtu函数中的xCompressCU后面)

    C++中如何进行txt文件的读入和写入_Williamhzw的博客-CSDN博客

    1. PelBuf piPredtest = bestCS->getPredBuf (_area);
    2. AreaBuf& recYpoint = piPredtest;
    3. std::ofstream out("C:\\Users\\qjjt\\Desktop\\输出.txt",std::ios::app);
    4. out << "当前CTU坐标与位置" << " X=" << posx << " Y=" << posy << " 宽=" << widthtest << " 高=" << heighttest << std::endl ;
    5. for(int i = 0;i < widthtest;i++)
    6. {
    7. for(int j = 0;j < heighttest;j++)
    8. {
    9. out<<"预测值 " << "x=" << i << "y=" << j << " " << recYpoint.at(i,j) << std::endl;
    10. }
    11. }

    思路:

    (1)首先新建 piPredtest来接收BestCS最优划分的预测值缓存。这个变量是PelBuf结构体创建的对象,主要是看当前area是区域信息还是区域中一个分量的信息,再来决定用PelBuf还是PelUnitBuf。可参考下面这个图,这几个函数专门用来存储缓存

      (2) 然后新建一个recYpoint来接收piPredtest中的信息,其实好像不用这一步,因为PelBuf就等于typedef  AreaBuf

    (3)接下来就是结果输出到文件中的一些操作,可参考上面博客。

    std::ios::app

    是为了在每次写入数据到文件中时,不覆盖已有数据。不然每次新运行就只会写入当前CTU的数据

    (4)最后就是CTU中每个像素点都得到其预测值,AreaBuf中有很多自带函数,如copyfrom(),at(),这里at()可读取当前CTU中指定像素的预测值

    遇到问题:

    【1】一开始预测值一直输出为0,所以直接查看bestCS。(在运行中查看自动窗口)

    bestCS->m_pred(这里面buf为-12581是因为我已经修改过了,原本为0),可看出缓存并没有传到这个最优bestCS里。所以如何得到预测缓存?

     (1)首先我查看getPredBuf的全部引用,到xCompressCU函数的第1001行找到了

    bestCS->picture->getPredBuf(currCsArea).copyFrom(bestCS->getPredBuf(currCsArea));

    然后做了测试

    //测试
      PelUnitBuf         piPredtest                     = bestCS->getPredBuf   (currCsArea);
      PelUnitBuf         piPredtest2                     = bestCS->picture->getPredBuf(currCsArea); 

    其中的buf都有值,说明在确定最优划分时预测值缓存确实已传输,但最终没到 最优划分的bestCS中

    (2)然后继续查看getPredBuf的全部引用调用层次结构,再根据之前写的图块划分结果显示,发现重建值的缓存可以调用,于是看到了其中的useSubStructure()函数并查看所有调用。发现这个函数在xCheckModeSplit()函数的1235行,cpyReco默认为true。而cpyPred由KEEP_PRED_AND_RESI_SIGNALS这个参数来决定。再根据以前写的关于useSubStructure()函数的博客,这个函数主要就跟缓存的传输相关,于是把KEEP_PRED_AND_RESI_SIGNALS这个参数从默认的0改为1.最终实现了预测值的显示

    VVC中图块划分结果在图像上显示(中间有一段没写完)_青椒鸡汤的博客-CSDN博客

    注意:

    这里useSubStructure()的具体作用应该再看一下,还有rd选择的两个函数也需要看完。放在后面,注意尽快写完。搞清楚bestCS->picture->getPredBuf(currCsArea)是传输到哪里去了

    xCheckModeSplit()函数中的RDcost(还没写,6.11写)_青椒鸡汤的博客-CSDN博客

    xCheckBestMode()和useModeResult()函数解析 (未写完,6.11写)_青椒鸡汤的博客-CSDN博客

    2.图片的预测值写入TXT

    因为存放预测和残差的缓存已设为1,所以应该也可以在compressGOP函数直接picture调用

    参考写法,代码中有这样的写法可以参考

    const CPelBuf    picOrig = pcPic->getOrigBuf (pcPic->block (compID));

    注意不能写成 pcPic->getPredBuf(),里面要有区域信息

    放在compressSlice()函数下面

    1. const CompArea &area = pcPic->Y();//亮度分量区域
    2. m_pcSliceEncoder->precompressSlice( pcPic );
    3. m_pcSliceEncoder->compressSlice ( pcPic, false, false );
    4. //测试
    5. PelBuf &piPredtest = pcPic->getPredBuf(area);//area为
    6. AreaBuf& predYpoint = piPredtest;
    7. std::ofstream out1("C:\\Users\\qjjt\\Desktop\\预测输出.txt");
    8. out1 << "当前帧" << " 宽=" << picWidth << " 高=" << picHeight << std::endl ;//注意输出文件的out分别命名
    9. for(int i = 0;i < picWidth;i++)
    10. {
    11. for(int j = 0;j < picHeight;j++)
    12. {
    13. out1 <<"预测值 " << "x=" << i << "y=" << j << " " << predYpoint.at(i,j) << std::endl;
    14. }
    15. }
    16. PelBuf &piResitest = pcPic->getResiBuf(area);
    17. AreaBuf& resiYpoint = piResitest;
    18. std::ofstream out2("C:\\Users\\qjjt\\Desktop\\残差输入.txt");
    19. out2 << "当前帧" << " 宽=" << picWidth << " 高=" << picHeight << std::endl ;
    20. for(int i = 0;i < picWidth;i++)
    21. {
    22. for(int j = 0;j < picHeight;j++)
    23. {
    24. out2 <<"残差值 " << "x=" << i << "y=" << j << " " << resiYpoint.at(i,j) << std::endl;
    25. }
    26. }
    27. PelBuf &piOrigtest = pcPic->getOrigBuf(area);
    28. AreaBuf& OrigYpoint = piOrigtest;
    29. std::ofstream out3("C:\\Users\\qjjt\\Desktop\\原始值输入.txt");
    30. out3 << "当前帧" << " 宽=" << picWidth << " 高=" << picHeight << std::endl ;
    31. for(int i = 0;i < picWidth;i++)
    32. {
    33. for(int j = 0;j < picHeight;j++)
    34. {
    35. out3 <<"原始值 " << "x=" << i << "y=" << j << " " << OrigYpoint.at(i,j) << std::endl;
    36. }
    37. }

     注意:这里我调用getPredBuf()时需要输出参数区域信息area,很麻烦。可以在picture.cpp

    记得在picture.h里面加上定义

     这样就可以直接使用getPredBuf()了

    3.图片预测值输出pred.yuv

    HEVC中类,对象和指向对象的指针_xidianliye的博客-CSDN博客

    因为编码完成时会输出码流str.bin和rec.yuv文件,所以通过找rec,找到了xCreateLib()函数

    1. void EncApp::xCreateLib( std::list& recBufList, const int layerId )
    2. {
    3. // Video I/O
    4. m_cVideoIOYuvInputFile.open( m_inputFileName, false, m_inputBitDepth, m_MSBExtendedBitDepth, m_internalBitDepth ); // read mode
    5. #if EXTENSION_360_VIDEO
    6. m_cVideoIOYuvInputFile.skipFrames(m_FrameSkip, m_inputFileWidth, m_inputFileHeight, m_InputChromaFormatIDC);
    7. #else
    8. const int sourceHeight = m_isField ? m_iSourceHeightOrg : m_sourceHeight;
    9. m_cVideoIOYuvInputFile.skipFrames(m_FrameSkip, m_sourceWidth - m_sourcePadding[0], sourceHeight - m_sourcePadding[1], m_InputChromaFormatIDC);
    10. #endif
    11. if (!m_reconFileName.empty())
    12. {
    13. if (m_packedYUVMode && ((m_outputBitDepth[CH_L] != 10 && m_outputBitDepth[CH_L] != 12)
    14. || ((m_sourceWidth & (1 + (m_outputBitDepth[CH_L] & 3))) != 0)))
    15. {
    16. EXIT ("Invalid output bit-depth or image width for packed YUV output, aborting\n");
    17. }
    18. if (m_packedYUVMode && (m_chromaFormatIDC != CHROMA_400) && ((m_outputBitDepth[CH_C] != 10 && m_outputBitDepth[CH_C] != 12)
    19. || (((m_sourceWidth / SPS::getWinUnitX (m_chromaFormatIDC)) & (1 + (m_outputBitDepth[CH_C] & 3))) != 0)))
    20. {
    21. EXIT ("Invalid chroma output bit-depth or image width for packed YUV output, aborting\n");
    22. }
    23. std::string reconFileName = m_reconFileName;
    24. if( m_reconFileName.compare( "/dev/null" ) && (m_maxLayers > 1) )
    25. {
    26. size_t pos = reconFileName.find_last_of('.');
    27. if (pos != string::npos)
    28. {
    29. reconFileName.insert( pos, std::to_string( layerId ) );
    30. }
    31. else
    32. {
    33. reconFileName.append( std::to_string( layerId ) );
    34. }
    35. }
    36. m_cVideoIOYuvReconFile.open( reconFileName, true, m_outputBitDepth, m_outputBitDepth, m_internalBitDepth ); // write mode
    37. }
    38. // create the encoder
    39. m_cEncLib.create( layerId );
    40. // create the output buffer
    41. for( int i = 0; i < (m_iGOPSize + 1 + (m_isField ? 1 : 0)); i++ )
    42. {
    43. recBufList.push_back( new PelUnitBuf );
    44. }
    45. }

    函数里的m_reconFileName就是重建yuv文件的默认名字,由此找到了recBufList。存储了重建数据,查看引用。

    可找到encode()中的writeoutput函数,这就是最后输出rec.yuv的函数

    1. void EncApp::xWriteOutput( int iNumEncoded, std::list& recBufList )
    2. {
    3. const InputColourSpaceConversion ipCSC = (!m_outputInternalColourSpace) ? m_inputColourSpaceConvert : IPCOLOURSPACE_UNCHANGED;
    4. std::list::iterator iterPicYuvRec = recBufList.end();
    5. int i;
    6. for ( i = 0; i < iNumEncoded; i++ )
    7. {
    8. --iterPicYuvRec;
    9. }
    10. if (m_isField)
    11. {
    12. //Reinterlace fields
    13. for ( i = 0; i < iNumEncoded/2; i++ )
    14. {
    15. const PelUnitBuf* pcPicYuvRecTop = *(iterPicYuvRec++);
    16. const PelUnitBuf* pcPicYuvRecBottom = *(iterPicYuvRec++);
    17. if (!m_reconFileName.empty())
    18. {
    19. m_cVideoIOYuvReconFile.write( *pcPicYuvRecTop, *pcPicYuvRecBottom,
    20. ipCSC,
    21. false, // TODO: m_packedYUVMode,
    22. m_confWinLeft, m_confWinRight, m_confWinTop, m_confWinBottom, NUM_CHROMA_FORMAT, m_isTopFieldFirst );
    23. }
    24. }
    25. }
    26. else
    27. {
    28. for ( i = 0; i < iNumEncoded; i++ )
    29. {
    30. const PelUnitBuf* pcPicYuvRec = *(iterPicYuvRec++);
    31. if (!m_reconFileName.empty())
    32. {
    33. if( m_cEncLib.isResChangeInClvsEnabled() && m_cEncLib.getUpscaledOutput() )
    34. {
    35. const SPS& sps = *m_cEncLib.getSPS( 0 );
    36. const PPS& pps = *m_cEncLib.getPPS( ( sps.getMaxPicWidthInLumaSamples() != pcPicYuvRec->get( COMPONENT_Y ).width || sps.getMaxPicHeightInLumaSamples() != pcPicYuvRec->get( COMPONENT_Y ).height ) ? ENC_PPS_ID_RPR : 0 );
    37. m_cVideoIOYuvReconFile.writeUpscaledPicture( sps, pps, *pcPicYuvRec, ipCSC, m_packedYUVMode, m_cEncLib.getUpscaledOutput(), NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
    38. }
    39. else
    40. {
    41. m_cVideoIOYuvReconFile.write( pcPicYuvRec->get( COMPONENT_Y ).width, pcPicYuvRec->get( COMPONENT_Y ).height, *pcPicYuvRec, ipCSC, m_packedYUVMode,
    42. m_confWinLeft, m_confWinRight, m_confWinTop, m_confWinBottom, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
    43. }
    44. }
    45. }
    46. }
    47. }

    再看recBufList在哪里改变的,可找到compressGOP函数中的xGetBuffer()函数

    1. void EncGOP::xGetBuffer( PicList& rcListPic,
    2. std::list& rcListPicYuvRecOut,
    3. int iNumPicRcvd,
    4. int iTimeOffset,
    5. Picture*& rpcPic,
    6. int pocCurr,
    7. bool isField )
    8. {
    9. int i;
    10. //const CompArea &areas = rpcPic->block(COMPONENT_Y);
    11. // Rec. output
    12. std::list::iterator iterPicYuvRec = rcListPicYuvRecOut.end();
    13. if (isField && pocCurr > 1 && m_iGopSize!=1)
    14. {
    15. iTimeOffset--;
    16. }
    17. int multipleFactor = m_pcCfg->getUseCompositeRef() ? 2 : 1;
    18. for (i = 0; i < (iNumPicRcvd * multipleFactor - iTimeOffset + 1); i += multipleFactor)
    19. {
    20. iterPicYuvRec--;
    21. }
    22. // Current pic.
    23. PicList::iterator iterPic = rcListPic.begin();
    24. while (iterPic != rcListPic.end())
    25. {
    26. rpcPic = *(iterPic);
    27. if( rpcPic->getPOC() == pocCurr && rpcPic->layerId == m_pcEncLib->getLayerId() )
    28. {
    29. break;
    30. }
    31. iterPic++;
    32. }
    33. CHECK(!(rpcPic != NULL), "Unspecified error");
    34. CHECK(!(rpcPic->getPOC() == pocCurr), "Unspecified error");
    35. (**iterPicYuvRec) = rpcPic->getRecoBuf();
    36. return;
    37. }

    最后一句(**iterPicYuvRec) = rpcPic->getRecoBuf();但这里有个问题,如果单纯的把getRecoBuf改成getPredBuf(),编译上没错,但会在编码时出错。这里可分别看一下getRecoBuf,getPredBuf的全部引用,可发现重建加了许多额外操作。所以可能要把这些全看懂,全改才能输出预测.YUV

    4.预测值的psnr

    如果想得到预测的psnr和BDrate,可看xCalculateAddPSNRs中的xCalculateAddPSNR函数,这里负责打印输出一些指标的值,把把getRecoBuf改成getPredBuf()即可。

    1. void EncGOP::xCalculateAddPSNRs( const bool isField, const bool isFieldTopFieldFirst,
    2. const int iGOPid, Picture* pcPic, const AccessUnit&accessUnit, PicList &rcListPic,
    3. const int64_t dEncTime, const InputColourSpaceConversion snr_conversion,
    4. const bool printFrameMSE, const bool printMSSSIM, double* PSNR_Y, bool isEncodeLtRef)
    5. {
    6. xCalculateAddPSNR(pcPic, pcPic->getRecoBuf(), accessUnit, (double)dEncTime, snr_conversion,
    7. printFrameMSE, printMSSSIM, PSNR_Y, isEncodeLtRef);
    8. //In case of field coding, compute the interlaced PSNR for both fields
    9. if(isField)
    10. {
    11. bool bothFieldsAreEncoded = false;
    12. int correspondingFieldPOC = pcPic->getPOC();
    13. int currentPicGOPPoc = m_pcCfg->getGOPEntry(iGOPid).m_POC;
    14. if(pcPic->getPOC() == 0)
    15. {
    16. // particular case for POC 0 and 1.
    17. // If they are not encoded first and separately from other pictures, we need to change this
    18. // POC 0 is always encoded first then POC 1 is encoded
    19. bothFieldsAreEncoded = false;
    20. }

    5.BDRATE的计算

    BD-rate计算原理_红玉圆圆圆的博客-CSDN博客_bdrate

    如何测试视频编码器的客观压缩性能(x264) - 墨天轮

    就去下提案JCTVC-K0279-v2,用里面那个excel表来计算BDrate。

     

     

  • 相关阅读:
    【函数】删除顺序表中第一个值等于x的元素
    OpenTiny Vue 3.11.0 发布:增加富文本、ColorPicker等4个新组件,迎来了贡献者大爆发!
    Java在算法题中的输入问题
    自用bat脚本,命令
    uverbs的交互方式——ioctl和write
    凉鞋的 Unity 笔记 204. 语句
    Semantic Kernel 通过 LocalAI 集成本地模型
    【入门】二分查找左侧边界
    Win10安装DBeaver连接MySQL8、导入和导出数据库详细教程
    初识C++内存管理
  • 原文地址:https://blog.csdn.net/dfhg54/article/details/126689968