最近在做关于3D-HEVC的实验,很想直观显示出最终的预测模式(PU)划分结果。最近看到了一个帖子,动了HEVC解码器部分,能直观显示出PU划分结果,但是,只能显示一帧(即解码的第一帧 I-Slice), 后面的全乱了。对于做帧间的是不够的,必须把每帧的划分结果直观地显示出来。仔细看了他的教程,我在此基础进行了改进,目前能够正常显示出HEVC、MV-HEVC的PU划分结果。在此,感谢网站(程光曦微)的作者。
1. PU模式划分显示效果图
2. HEVC decoder 代码修改
该程序是基于HM 11.0/3D-HTM的,废话不多说了,给出修改代码步骤。
2.1 PU划分数据结构体定义及宏定义
为了不改变源代码程序逻辑,我们所有修改的代码都放在自己定义的预处理命令宏定义中。
在TypeDef.h中,定义自己的条件编译预处理命令。
#define statistics_mode 1//用于统计输出块信息
#if statistics_mode
struct PtPair
{
unsigned int _pt1x;
unsigned int _pt1y;
unsigned int _pt2x;
unsigned int _pt2y;
unsigned char mode;
};
#endif
2.2 PU划分及mode向量定义
为了保存各帧CU及mode最终划分结果,在TAppDecTop.h文件中定义如下代码:
class TAppDecTop : public TAppDecCfg
{
private:
#if statistics_mode
std::vector
m_plistPt;//用于保存CU划分及mode结果
std::vector
flag;//用于保存每帧的mode数量
#endif
2.3 修改各函数头,将m_plistPt传递进去
在TAppDecTop.cpp文件中, 按照下面的方式替换源代码:
//原来的代码
bNewPicture = m_cTDecTop.decode(nalu, m_iSkipFrame, m_iPOCLastDisplay);
//修改成如下代码
#if statistics_mode
bNewPicture = m_cTDecTop.decode(nalu, m_iSkipFrame, m_iPOCLastDisplay,m_plistPt);
#else
bNewPicture = m_cTDecTop.decode(nalu, m_iSkipFrame, m_iPOCLastDisplay);
#endif
编译程序,肯定会出错,因为没有修改相应的函数头。在TDecTop.cpp文件中,按照下面的方式修改decode函数头:
#if statistics_mode
Bool TDecTop::decode(InputNALUnit& nalu, Int& iSkipFrame, Int& iPOCLastDisplay,std::vector
& list)
#else
Bool TDecTop::decode(InputNALUnit& nalu, Int& iSkipFrame, Int& iPOCLastDisplay)
#endif
#if statistics_mode
Bool TDecTop::decode(InputNALUnit& nalu, Int& iSkipFrame, Int& iPOCLastDisplay,std::vector
& list);
#else
Bool decode(InputNALUnit& nalu, Int& iSkipFrame, Int& iPOCLastDisplay);
#endif
接下来,将m_plistPt传递到xDecodeSlice函数中,将decode函数中的xDecodeSlice(nalu, iSkipFrame, iPOCLastDisplay)修改成xDecodeSlice(nalu, iSkipFrame, iPOCLastDisplay,list);即将m_plistPt传递到xDecodeSlice函数中。同样地,需要修改相应的函数头,在TDecTop.cpp文件中,修改对应的函数头,按照下面方式修改:
#if statistics_mode
Bool TDecTop::xDecodeSlice(InputNALUnit &nalu, Int &iSkipFrame, Int iPOCLastDisplay,std::vector
& list )
#else
Bool TDecTop::xDecodeSlice(InputNALUnit &nalu, Int &iSkipFrame, Int iPOCLastDisplay )
#endif
在TDecTop.h中,按照下面方式替换DecodeSlice函数声明:
#if statistics_mode
Bool TDecTop::xDecodeSlice(InputNALUnit &nalu, Int &iSkipFrame, Int iPOCLastDisplay,std::vector
& list );
#else
Bool xDecodeSlice(InputNALUnit &nalu, Int &iSkipFrame, Int iPOCLastDisplay);
#endif
接下来,在xDecodeSlice函数中,将m_cGopDecoder.decompressSlice(nalu.m_Bitstream, pcPic);替换成如下代码:
// Decode a picture
#if statistics_mode
m_cGopDecoder.decompressSlice(nalu.m_Bitstream, pcPic,list);
#else
m_cGopDecoder.decompressSlice(nalu.m_Bitstream, pcPic);
#endif
#if statistics_mode
Void TDecGop::decompressSlice(TComInputBitstream* pcBitstream, TComPic*& rpcPic, std::vector
& list)
#else
Void TDecGop::decompressSlice(TComInputBitstream* pcBitstream, TComPic*& rpcPic)
#endif
#if statistics_mode
Void TDecGop::decompressSlice(TComInputBitstream* pcBitstream, TComPic*& rpcPic, std::vector
& list);
#else
Void decompressSlice(TComInputBitstream* pcBitstream, TComPic*& rpcPic );
#endif
在decompressSlice函数中,将m_pcSliceDecoder->decompressSlice( ppcSubstreams, rpcPic, m_pcSbacDecoder, m_pcSbacDecoders) 修改成如下代码:
#if statistics_mode
m_pcSliceDecoder->decompressSlice( ppcSubstreams, rpcPic, m_pcSbacDecoder, m_pcSbacDecoders, list);
#else
m_pcSliceDecoder->decompressSlice( ppcSubstreams, rpcPic, m_pcSbacDecoder, m_pcSbacDecoders);
#endif
接着,修改相应的函数头函数声明:
在TDecSlice.cpp文件中,
在TDecSlice.h文件中,
#if statistics_mode
Void TDecSlice::decompressSlice(TComInputBitstream** ppcSubstreams, TComPic*& rpcPic, TDecSbac* pcSbacDecoder, TDecSbac* pcSbacDecoders, std::vector
& list)
#else
Void TDecSlice::decompressSlice(TComInputBitstream** ppcSubstreams, TComPic*& rpcPic, TDecSbac* pcSbacDecoder, TDecSbac* pcSbacDecoders)
#endif
#if statistics_mode
Void TDecSlice::decompressSlice(TComInputBitstream** ppcSubstreams, TComPic*& rpcPic, TDecSbac* pcSbacDecoder, TDecSbac* pcSbacDecoders, std::vector
& list);
#else
Void decompressSlice ( TComInputBitstream** ppcSubstreams, TComPic*& rpcPic, TDecSbac* pcSbacDecoder, TDecSbac* pcSbacDecoders );
#endif
接下来,将decompressSlice函数中的m_pcCuDecoder->decompressCU ( pcCU );修改成如下代码:
#if statistics_mode
m_pcCuDecoder->decodeCU ( pcCU, uiIsLast, list);
m_pcCuDecoder->decompressCU ( pcCU , list);
#else
m_pcCuDecoder->decodeCU ( pcCU, uiIsLast );
m_pcCuDecoder->decompressCU ( pcCU );
#endif
在TDecCu.cpp文件中,
#if statistics_mode
Void TDecCu::decompressCU( TComDataCU* pcCU,std::vector
& list )
#else
Void TDecCu::decompressCU( TComDataCU* pcCU )
#endif
{
#if statistics_mode
xDecompressCU( pcCU, 0, 0,list );
#else
xDecompressCU( pcCU, 0, 0);
#enif
}
/// reconstruct CU information
#if statistics_mode
Void decompressCU( TComDataCU* pcCU,std::vector
& list );
#else
Void decompressCU ( TComDataCU* pcCU );
#endif
#if statistics_mode
Void xDecompressCU( TComDataCU* pcCU, UInt uiAbsPartIdx, UInt uiDepth,std::vector
& list );
#else
Void xDecompressCU ( TComDataCU* pcCU, UInt uiAbsPartIdx, UInt uiDepth );
#endif
2.4 保存CU划分和mode选择结果
在xDecompressCU函数中,保存每一帧的CU划分结果及mode结果,在if( ( ( uiDepth < pcCU->getDepth( uiAbsPartIdx ) ) && ( uiDepth < g_uiMaxCUDepth - g_uiAddCUDepth ) ) || bBoundary )结束后,添加如下代码保存结果:
//==================================================================================//
//==================================模式大小统计====================================//
//==================================================================================//
#if statistics_mode
struct PtPair tmp;
tmp._pt1x=uiLPelX; tmp._pt1y=uiTPelY; tmp._pt2x=uiRPelX; tmp._pt2y=uiBPelY;tmp.mode=*pcCU->getPartitionSize();
list.push_back(tmp);
#endif
if(binSlice&&( uiLPelX < pcSlice->getSPS()->getPicWidthInLumaSamples() ) && ( uiTPelY < pcSlice->getSPS()->getPicHeightInLumaSamples() ) )
{
#if statistics_mode
xDecompressCU(pcCU, uiIdx, uiNextDepth,list );
#else
xDecompressCU(pcCU, uiIdx, uiNextDepth);
#endif
}
2.5 PU划分结果显示
在decode()函数中修改xFlushOutput和xWriteOutput函数调用,
现在到了最后关头,也是最重要的一部分,贴出xWriteOutput和xFlushOutput函数全部代码,不想具体说了,对照代码修改吧!
#if statistics_mode
xFlushOutput( pcListPic,m_plistPt,flag );
#else
xFlushOutput( pcListPic);
#endif
#if statistics_mode
unsigned long temp=0;
for(int i=0;i
接着,修改xFlushOutput和xWriteOutput函数头和声明,
//TAppDecTop.cpp中修改对应的函数头
#if statistics_mode
Void TAppDecTop::xWriteOutput( TComList
* pcListPic, UInt tId,std::vector
& list,std::vector
& flag)
#else
Void TAppDecTop::xWriteOutput( TComList
* pcListPic, UInt tId)
#endif
#if statistics_mode
Void TAppDecTop::xFlushOutput( TComList
* pcListPic,std::vector
& list,std::vector
& flag ) #else Void TAppDecTop::xFlushOutput( TComList
* pcListPic ) #endif //TAppDecTop.h中修改对应的函数声明 #if statistics_mode Void TAppDecTop::xWriteOutput( TComList
* pcListPic, UInt tId,std::vector
& list,std::vector
& flag); Void TAppDecTop::xFlushOutput( TComList
* pcListPic,std::vector
& list,std::vector
& flag ); #else Void xWriteOutput ( TComList
* pcListPic , UInt tId); ///< write YUV to file Void xFlushOutput ( TComList
* pcListPic ); ///< flush all remaining decoded pictures to file #endif
#if statistics_mode
Void TAppDecTop::xWriteOutput( TComList
* pcListPic, UInt tId,std::vector
& list,std::vector
& flag)
#else
Void TAppDecTop::xWriteOutput( TComList
* pcListPic, UInt tId)
#endif
#endif
{
TComList
::iterator iterPic = pcListPic->begin();
Int not_displayed = 0;
while (iterPic != pcListPic->end())
{
TComPic* pcPic = *(iterPic);
#if H_MV
if(pcPic->getOutputMark() && pcPic->getPOC() > m_pocLastDisplay[decIdx])
#else
if(pcPic->getOutputMark() && pcPic->getPOC() > m_iPOCLastDisplay)
#endif
{
not_displayed++;
}
iterPic++;
}
iterPic = pcListPic->begin();
while (iterPic != pcListPic->end())
{
TComPic* pcPic = *(iterPic);
#if H_MV
if ( pcPic->getOutputMark() && (not_displayed > pcPic->getNumReorderPics(tId) && pcPic->getPOC() > m_pocLastDisplay[decIdx]))
#else
if ( pcPic->getOutputMark() && (not_displayed > pcPic->getNumReorderPics(tId) && pcPic->getPOC() > m_iPOCLastDisplay))
#endif
{
// write to file
not_displayed--;
#if statistics_mode
TComPicYuv *p_dstyuv=new TComPicYuv;
TComPicYuv *p_orgyuv=pcPic->getPicYuvRec();
p_dstyuv->create(p_orgyuv->getWidth(),p_orgyuv->getHeight(),1,1,0);
p_orgyuv->copyToPic(p_dstyuv);
Pel *pY=p_dstyuv->getLumaAddr();
UInt stride = p_dstyuv->getStride();
for(UInt index = 0; index < flag[0]; index++)
{
for(UInt y = list[index]._pt1y; y <= list[index]._pt2y; y++)
{
for(UInt x = list[index]._pt1x; x <= list[index]._pt2x; x++)
{
switch(list[index].mode)
{
case SIZE_2Nx2N:
if(y == list[index]._pt1y )
pY[y*stride + x] = 0;
if(x == list[index]._pt1x )
pY[y*stride + x] = 0;
break;
case SIZE_2NxN:
if(y == list[index]._pt1y )
pY[y*stride + x] = 0;
if(y == list[index]._pt1y+(list[index]._pt2y-list[index]._pt1y+1)/2)
pY[y*stride + x] = 0;
if(x == list[index]._pt1x )
pY[y*stride + x] = 0;
break;
case SIZE_Nx2N:
if(y == list[index]._pt1y )
pY[y*stride + x] = 0;
if(x == list[index]._pt1x+(list[index]._pt2x-list[index]._pt1x+1)/2)
pY[y*stride + x] = 0;
if(x == list[index]._pt1x )
pY[y*stride + x] = 0;
break;
case SIZE_NxN:
if(y == list[index]._pt1y )
pY[y*stride + x] = 0;
if(y == list[index]._pt1y+(list[index]._pt2y-list[index]._pt1y+1)/2)
pY[y*stride + x] = 0;
if(x == list[index]._pt1x+(list[index]._pt2x-list[index]._pt1x+1)/2)
pY[y*stride + x] = 0;
if(x == list[index]._pt1x )
pY[y*stride + x] = 0;
break;
case SIZE_2NxnU:
if(y == list[index]._pt1y )
pY[y*stride + x] = 0;
if(y == list[index]._pt1y+(list[index]._pt2y-list[index]._pt1y+1)/4)
pY[y*stride + x] = 0;
if(x == list[index]._pt1x )
pY[y*stride + x] = 0;
break;
case SIZE_2NxnD:
if(y == list[index]._pt1y )
pY[y*stride + x] = 0;
if(y == list[index]._pt1y+3*(list[index]._pt2y-list[index]._pt1y+1)/4)
pY[y*stride + x] = 0;
if(x == list[index]._pt1x )
pY[y*stride + x] = 0;
break;
case SIZE_nLx2N:
if(y == list[index]._pt1y )
pY[y*stride + x] = 0;
if(x == list[index]._pt1x+(list[index]._pt2x-list[index]._pt1x+1)/4)
pY[y*stride + x] = 0;
if(x == list[index]._pt1x )
pY[y*stride + x] = 0;
break;
case SIZE_nRx2N:
if(y == list[index]._pt1y )
pY[y*stride + x] = 0;
if(x == list[index]._pt1x+3*(list[index]._pt2x-list[index]._pt1x+1)/4)
pY[y*stride + x] = 0;
if(x == list[index]._pt1x )
pY[y*stride + x] = 0;
break;
default:
break;
}
}
}
}
if(flag.size()>1)
{
list.erase(list.begin(),list.begin()+flag[0]);
flag.erase(flag.begin());
}
#endif
#if H_MV
if ( m_pchReconFiles[decIdx] )
#else
if ( m_pchReconFile )
#endif
{
const Window &conf = pcPic->getConformanceWindow();
const Window &defDisp = m_respectDefDispWindow ? pcPic->getDefDisplayWindow() : Window();
#if H_MV
#if H_MV5
assert( conf .getScaledFlag() );
assert( defDisp.getScaledFlag() );
#endif
#if statistics_mode
m_tVideoIOYuvReconFile[decIdx]->write( p_dstyuv,
#else
m_tVideoIOYuvReconFile[decIdx]->write( pcPic->getPicYuvRec(),
#endif
#else
#if statistics_mode
m_cTVideoIOYuvReconFile.write( p_dstyuv,
#else
m_cTVideoIOYuvReconFile.write( pcPic->getPicYuvRec(),
#endif
#endif
conf.getWindowLeftOffset() + defDisp.getWindowLeftOffset(),
conf.getWindowRightOffset() + defDisp.getWindowRightOffset(),
conf.getWindowTopOffset() + defDisp.getWindowTopOffset(),
conf.getWindowBottomOffset() + defDisp.getWindowBottomOffset() );
}
// update POC of display order
#if H_MV
m_pocLastDisplay[decIdx] = pcPic->getPOC();
#else
m_iPOCLastDisplay = pcPic->getPOC();
#endif
// erase non-referenced picture in the reference picture list after display
if ( !pcPic->getSlice(0)->isReferenced() && pcPic->getReconMark() == true )
{
#if !DYN_REF_FREE
pcPic->setReconMark(false);
// mark it should be extended later
pcPic->getPicYuvRec()->setBorderExtension( false );
#else
pcPic->destroy();
pcListPic->erase( iterPic );
iterPic = pcListPic->begin(); // to the beginning, non-efficient way, have to be revised!
continue;
#endif
}
pcPic->setOutputMark(false);
}
iterPic++;
}
}
/** \param pcListPic list of pictures to be written to file
\todo DYN_REF_FREE should be revised
*/
#if H_MV
#if statistics_mode
Void TAppDecTop::xFlushOutput( TComList
* pcListPic, Int decIdx,std::vector
& list,std::vector
& flag) #else Void TAppDecTop::xFlushOutput( TComList
* pcListPic, Int decIdx) #endif #else #if statistics_mode Void TAppDecTop::xFlushOutput( TComList
* pcListPic,std::vector
& list,std::vector
& flag ) #else Void TAppDecTop::xFlushOutput( TComList
* pcListPic ) #endif #endif { if(!pcListPic) { return; } TComList
::iterator iterPic = pcListPic->begin(); iterPic = pcListPic->begin(); while (iterPic != pcListPic->end()) { TComPic* pcPic = *(iterPic); if ( pcPic->getOutputMark() ) { // write to file #if statistics_mode TComPicYuv *p_dstyuv=new TComPicYuv; TComPicYuv *p_orgyuv=pcPic->getPicYuvRec(); p_dstyuv->create(p_orgyuv->getWidth(),p_orgyuv->getHeight(),1,1,0); p_orgyuv->copyToPic(p_dstyuv); Pel *pY=p_dstyuv->getLumaAddr(); UInt stride = p_dstyuv->getStride(); for(UInt index = 0; index < flag[0]; index++) { for(UInt y = list[index]._pt1y; y <= list[index]._pt2y; y++) { for(UInt x = list[index]._pt1x; x <= list[index]._pt2x; x++) { switch(list[index].mode) { case SIZE_2Nx2N: if(y == list[index]._pt1y ) pY[y*stride + x] = 0; if(x == list[index]._pt1x ) pY[y*stride + x] = 0; break; case SIZE_2NxN: if(y == list[index]._pt1y ) pY[y*stride + x] = 0; if(y == list[index]._pt1y+(list[index]._pt2y-list[index]._pt1y+1)/2) pY[y*stride + x] = 0; if(x == list[index]._pt1x ) pY[y*stride + x] = 0; break; case SIZE_Nx2N: if(y == list[index]._pt1y) pY[y*stride + x] = 0; if(x == list[index]._pt1x+(list[index]._pt2x-list[index]._pt1x+1)/2) pY[y*stride + x] = 0; if(x == list[index]._pt1x) pY[y*stride + x] = 0; break; case SIZE_NxN: if(y == list[index]._pt1y) pY[y*stride + x] = 0; if(y == list[index]._pt1y+(list[index]._pt2y-list[index]._pt1y+1)/2) pY[y*stride + x] = 0; if(x == list[index]._pt1x+(list[index]._pt2x-list[index]._pt1x+1)/2) pY[y*stride + x] = 0; if(x == list[index]._pt1x ) pY[y*stride + x] = 0; break; case SIZE_2NxnU: if(y == list[index]._pt1y ) pY[y*stride + x] = 0; if(y == list[index]._pt1y+(list[index]._pt2y-list[index]._pt1y+1)/4) pY[y*stride + x] = 0; if(x == list[index]._pt1x ) pY[y*stride + x] = 0; break; case SIZE_2NxnD: if(y == list[index]._pt1y ) pY[y*stride + x] = 0; if(y == list[index]._pt1y+3*(list[index]._pt2y-list[index]._pt1y+1)/4) pY[y*stride + x] = 0; if(x == list[index]._pt1x) pY[y*stride + x] = 0; break; case SIZE_nLx2N: if(y == list[index]._pt1y) pY[y*stride + x] = 0; if(x == list[index]._pt1x+(list[index]._pt2x-list[index]._pt1x+1)/4) pY[y*stride + x] = 0; if(x == list[index]._pt1x) pY[y*stride + x] = 0; break; case SIZE_nRx2N: if(y == list[index]._pt1y ) pY[y*stride + x] = 0; if(x == list[index]._pt1x+3*(list[index]._pt2x-list[index]._pt1x+1)/4) pY[y*stride + x] = 0; if(x == list[index]._pt1x ) pY[y*stride + x] = 0; break; default: break; } } } } if(flag.size()>1) { list.erase(list.begin(),list.begin()+flag[0]); flag.erase(flag.begin()); } #endif #if H_MV if ( m_pchReconFiles[decIdx] ) #else if ( m_pchReconFile ) #endif { const Window &conf = pcPic->getConformanceWindow(); const Window &defDisp = m_respectDefDispWindow ? pcPic->getDefDisplayWindow() : Window(); #if H_MV #if H_MV5 assert( conf .getScaledFlag() ); assert( defDisp.getScaledFlag() ); #endif #if statistics_mode m_tVideoIOYuvReconFile[decIdx]->write( p_dstyuv, #else m_tVideoIOYuvReconFile[decIdx]->write( pcPic->getPicYuvRec(), #endif #else #if statistics_mode m_cTVideoIOYuvReconFile.write( p_dstyuv, #else m_cTVideoIOYuvReconFile.write( pcPic->getPicYuvRec(), #endif #endif conf.getWindowLeftOffset() + defDisp.getWindowLeftOffset(), conf.getWindowRightOffset() + defDisp.getWindowRightOffset(), conf.getWindowTopOffset() + defDisp.getWindowTopOffset(), conf.getWindowBottomOffset() + defDisp.getWindowBottomOffset() ); } // update POC of display order #if H_MV m_pocLastDisplay[decIdx] = pcPic->getPOC(); #else m_iPOCLastDisplay = pcPic->getPOC(); #endif // erase non-referenced picture in the reference picture list after display if ( !pcPic->getSlice(0)->isReferenced() && pcPic->getReconMark() == true ) { #if !DYN_REF_FREE pcPic->setReconMark(false); // mark it should be extended later pcPic->getPicYuvRec()->setBorderExtension( false ); #else pcPic->destroy(); pcListPic->erase( iterPic ); iterPic = pcListPic->begin(); // to the beginning, non-efficient way, have to be revised! continue; #endif } pcPic->setOutputMark(false); } #if !H_MV #if !DYN_REF_FREE if(pcPic) { pcPic->destroy(); delete pcPic; pcPic = NULL; } #endif #endif iterPic++; } #if H_MV m_pocLastDisplay[decIdx] = -MAX_INT; #else pcListPic->clear(); m_iPOCLastDisplay = -MAX_INT; #endif }
2.6 解码看结果
将先编码过后的文件,用解码器解码,就会看到最终结果。有的可能不知道怎样使用解码器,贴出命令行供参考
TAppDecoder -b 2Dmodes.bin -o 2Dmodes.yuv (TAppDecoder解码器应用程序,2Dmodes.bin编码器输出的压缩文件,2Dmodes.yuv 为重建文件名,2Dmodes.yuv 总的2Dmodes可以任意取名)。
3.
如果不想自己修改代码,我这有编译好的解码器,解码过后,就会看到像我们展示出的样例结果,是不是很直观吧!该解码器在以下QQ群:101118126
本文介绍了如何改进HEVC解码器,以直观显示每帧的预测模式(PU)划分结果。通过调整代码,包括PU划分的数据结构、mode向量定义以及函数修改,成功实现了对HEVC和MV-HEVC的PU划分显示,使得每一帧的解码结果都能清晰呈现。

3808

被折叠的 条评论
为什么被折叠?



