可参考这两篇博客:
VVC学习之四:VTM中的数据结构——描述_Aidoneus_y的博客-优快云博客
VTM1.0代码阅读:CodingStructure类主要函数_矛盾统一的博客-优快云博客
主要作用:将子cu的最优模式信息copy到tempCS中
void CodingStructure::useSubStructure( const CodingStructure& subStruct, const ChannelType chType, const UnitArea &subArea, const bool cpyPred /*= true*/, const bool cpyReco /*= true*/, const bool cpyOrgResi /*= true*/, const bool cpyResi /*= true*/, const bool updateCost /*= true*/ )
{
//获取正在压缩的区域信息
UnitArea clippedArea = clipArea( subArea, *picture );
setDecomp( clippedArea );
CPelUnitBuf subPredBuf = cpyPred ? subStruct.getPredBuf( clippedArea ) : CPelUnitBuf();
CPelUnitBuf subResiBuf = cpyResi ? subStruct.getResiBuf( clippedArea ) : CPelUnitBuf();
CPelUnitBuf subRecoBuf = cpyReco ? subStruct.getRecoBuf( clippedArea ) : CPelUnitBuf();
// 如果父CU存在
if( parent )
{
// copy data to picture
if (cpyPred)
{
getPredBuf(clippedArea).copyFrom(subPredBuf);
}
if (cpyResi)
{
getResiBuf(clippedArea).copyFrom(subResiBuf);
}
if (cpyReco)
{
getRecoBuf(clippedArea).copyFrom(subRecoBuf);
}
if (cpyOrgResi)
{
getOrgResiBuf(clippedArea).copyFrom(subStruct.getOrgResiBuf(clippedArea));
}
}
if (cpyPred)
{
picture->getPredBuf(clippedArea).copyFrom(subPredBuf);
}
if (cpyResi)
{
picture->getResiBuf(clippedArea).copyFrom(subResiBuf);
}
if (cpyReco)
{
picture->getRecoBuf(clippedArea).copyFrom(subRecoBuf);
}
if (!subStruct.m_isTuEnc && ((!slice->isIntra() || slice->getSPS()->getIBCFlag()) && chType != CHANNEL_TYPE_CHROMA))
{
// copy motion buffer
MotionBuf ownMB = getMotionBuf ( clippedArea );
CMotionBuf subMB = subStruct.getMotionBuf( clippedArea );
ownMB.copyFrom( subMB );
motionLut = subStruct.motionLut;
}
prevPLT = subStruct.prevPLT;
if ( updateCost )
{
fracBits += subStruct.fracBits;
dist += subStruct.dist;
cost += subStruct.cost;
costDbOffset += subStruct.costDbOffset;
}
if( parent )
{
// allow this to be false at the top level
CHECKD( !area.contains( subArea ), "Trying to use a sub-structure not contained in self" );
}
// copy the CUs over
if( subStruct.m_isTuEnc )
{
// don't copy if the substruct was created for encoding of the TUs
}
else
{
for( const auto &pcu : subStruct.cus )
{
// add an analogue CU into own CU store
const UnitArea &cuPatch = *pcu;
CodingUnit &cu = addCU( cuPatch, pcu->chType );
// copy the CU info from subPatch
cu = *pcu;
}
}
// copy the PUs over
if( subStruct.m_isTuEnc )
{
// don't copy if the substruct was created for encoding of the TUs
}
else
{
for( const auto &ppu : subStruct.pus )
{
// add an analogue PU into own PU store
const UnitArea &puPatch = *ppu;
PredictionUnit &pu = addPU( puPatch, ppu->chType );
// copy the PU info from subPatch
pu = *ppu;
}
}
// copy the TUs over
for( const auto &ptu : subStruct.tus )
{
// add an analogue TU into own TU store
const UnitArea &tuPatch = *ptu;
TransformUnit &tu = addTU( tuPatch, ptu->chType );
// copy the TU info from subPatch
tu = *ptu;
}
}
CU整个区域的setDecomp()函数
void CodingStructure::setDecomp(const UnitArea &_area, const bool _isCoded /*= true*/)
{
//三个分量
for( uint32_t i = 0; i < _area.blocks.size(); i++ )
{
//如果当前CU正常
if (_area.blocks[i].valid())
{
setDecomp(_area.blocks[i], _isCoded);
}
}
}
1.1 CU单独YCbCr分量的setDecomp()函数
void CodingStructure::setDecomp(const CompArea &_area, const bool _isCoded /*= true*/)
{
//根据当前区域分量的类别设置scale
const UnitScale& scale = unitScale[_area.compID];
AreaBuf<bool> isCodedBlk( m_isDecomp[toChannelType( _area.compID )] + rsAddr( _area, area.blocks[_area.compID].pos(), area.blocks[_area.compID].width, scale ),
area.blocks[_area.compID].width >> scale.posx,
_area.width >> scale.posx,
_area.height >> scale.posy);
isCodedBlk.fill( _isCoded );
}
这里出现一个stride,注意:这里area表示当前CU的父CU区域,_area才表示当前CU区域。所以此处stride表示父CU区域的宽度
最终以{(Y) x = 0, y = 0, width = 8, height = 4}为例,当前
名称 | 值 | 类型 | |
---|---|---|---|
◢ | isCodedBlk | {width = 2, heigth = 1, stride = 4} | AreaBuf<bool> |
且isDecomp设为false。并把这些数值存入AreaBuf<>这个容器中
inline size_t rsAddr(const Position &pos, const Position &origin, const uint32_t stride, const UnitScale &unitScale )
{
return (stride >> unitScale.posx) * ((pos.y - origin.y) >> unitScale.posy) + ((pos.x - origin.x) >> unitScale.posx);
}
这里返回的rsAddr应为光栅扫描模式下当前CU在父CU中的地址顺序
Y分量的stride缩小4倍。CbCr分量好像不执行,invalid
1.2
三个CPelUnitBuf:猜测是CUpixel 的意思,将CU的三个分量的值都存进这个缓冲区内,这里的
getRecoBuf()是,与下面直接getRecoBuf()调用的函数不同,推测这里是subStruct调用getRecoBuf()函数,得到的CPelUnitBuf赋给subRecoBuf。等于是将当前子CU的信息赋给了subRecoBuf
const CPelUnitBuf CodingStructure::getRecoBuf(const UnitArea &unit) const { return getBuf(unit, PIC_RECONSTRUCTION);
const CPelUnitBuf CodingStructure::getBuf( const UnitArea &unit, const PictureType &type ) const
{
// no parent fetching for buffers
if( area.chromaFormat == CHROMA_400 )
{
return CPelUnitBuf( area.chromaFormat, getBuf( unit.Y(), type ) );
}
else
{
return CPelUnitBuf( area.chromaFormat, getBuf( unit.Y(), type ), getBuf( unit.Cb(), type ), getBuf( unit.Cr(), type ) );
}
}
if语句:如果存在父结点,根据cpyPred,cpyResi,cpyReco,cpyOrgResi的值,确定是否将当前子CU的不同类型的BUF里的值,如subRecoBuf存到PelUnitBuf里
PelUnitBuf CodingStructure::getRecoBuf(const UnitArea &unit) { return getBuf(unit, PIC_RECONSTRUCTION);
PelUnitBuf CodingStructure::getBuf( const UnitArea &unit, const PictureType &type )
{
// no parent fetching for buffers
if( area.chromaFormat == CHROMA_400 )
{
return PelUnitBuf( area.chromaFormat, getBuf( unit.Y(), type ) );
}
else
{
return PelUnitBuf( area.chromaFormat, getBuf( unit.Y(), type ), getBuf( unit.Cb(), type ), getBuf( unit.Cr(), type ) );
}
}
第二个if语句:将前面PelUnitBuf的值存进picture所调用的PelUnitBuf里,注意这几个getBuf()函数的不同
PelUnitBuf Picture::getRecoBuf(const UnitArea &unit, bool wrap) { return getBuf(unit, wrap ? PIC_RECON_WRAP : PIC_RECONSTRUCTION); }
PelUnitBuf Picture::getBuf( const UnitArea &unit, const PictureType &type )
{
if( chromaFormat == CHROMA_400 )
{
return PelUnitBuf( chromaFormat, getBuf( unit.Y(), type ) );
}
else
{
return PelUnitBuf( chromaFormat, getBuf( unit.Y(), type ), getBuf( unit.Cb(), type ), getBuf( unit.Cr(), type ) );
}
}
注意看得到的数据:buffer里存储了亮度分量的一些数据,有stride
第三个if语句:
else
{
for( const auto &pcu : subStruct.cus )
{
// add an analogue CU into own CU store
const UnitArea &cuPatch = *pcu;
CodingUnit &cu = addCU( cuPatch, pcu->chType );
// copy the CU info from subPatch
cu = *pcu;
}
}
遍历subStruct.cus,用pcu给每一个cus赋值。pcu有当前子CU的信息
CodingUnit& CodingStructure::addCU( const UnitArea &unit, const ChannelType chType )
{
CodingUnit *cu = m_cuCache.get();
//将cu一些信息初始化
cu->UnitArea::operator=( unit );
cu->initData();
cu->cs = this;
cu->slice = nullptr;
cu->next = nullptr;
cu->firstPU = nullptr;
cu->lastPU = nullptr;
cu->firstTU = nullptr;
cu->lastTU = nullptr;
cu->chType = chType;
cu->treeType = treeType;
cu->modeType = modeType;
//前面的CU?
CodingUnit *prevCU = m_numCUs > 0 ? cus.back() : nullptr;
if( prevCU )
{
prevCU->next = cu;
}
cus.push_back( cu );
uint32_t idx = ++m_numCUs;
cu->idx = idx;
uint32_t numCh = ::getNumberValidChannels( area.chromaFormat );
for( uint32_t i = 0; i < numCh; i++ )
{
if( !cu->blocks[i].valid() )
{
continue;
}
//当前CU的父CU area
const CompArea &_selfBlk = area.blocks[i];
//当前CU的 area
const CompArea &_blk = cu-> blocks[i];
//当前CU的缩放比例
const UnitScale& scale = unitScale[_blk.compID];
//缩放后的父CU area
const Area scaledSelf = scale.scale( _selfBlk );
//缩放后的CU area
const Area scaledBlk = scale.scale( _blk );
//一个地址信息
unsigned *idxPtr = m_cuIdx[i] + rsAddr( scaledBlk.pos(), scaledSelf.pos(), scaledSelf.width );
CHECK( *idxPtr, "Overwriting a pre-existing value, should be '0'!" );
//将数据存进AreaBuf
AreaBuf<uint32_t>( idxPtr, scaledSelf.width, scaledBlk.size() ).fill( idx );
}
return *cu;
}
这个addCU()函数作用好像是初始化 pcu的一些数据,然后据此推测出缩放后的CU,父CU的size,和idxptr,将这些数据存入AreaBuf
最后返回*cu=*pcu,再赋给cus。
VVC中图块划分结果在图像上显示(中间有一段没写完)_青椒鸡汤的博客-优快云博客
也有一部分解释
主要目的:推测应该是得到AreaBuf的那几个数据
2. releaseIntermediateData()函数:
void CodingStructure::clearTUs()
{
//查看色彩格式并得到通道数
int numCh = ::getNumberValidChannels( area.chromaFormat );
for( int i = 0; i < numCh; i++ )
{
//等于当前TU所属分量的大小向右移动所属的刻度位数
size_t _area = ( area.blocks[i].area() >> unitScale[i].area );
memset( m_isDecomp[i], false, sizeof( *m_isDecomp[0] ) * _area );
memset( m_tuIdx [i], 0, sizeof( *m_tuIdx [0] ) * _area );
}
numCh = getNumberValidComponents( area.chromaFormat );
//各分量清零
for( int i = 0; i < numCh; i++ )
{
m_offsets[i] = 0;
}
//清零
for( auto &pcu : cus )
{
pcu->firstTU = pcu->lastTU = nullptr;
}
m_tuCache.cache( tus );
m_numTUs = 0;
}
void CodingStructure::clearPUs()
{
int numCh = ::getNumberValidChannels( area.chromaFormat );
for( int i = 0; i < numCh; i++ )
{
memset( m_puIdx[i], 0, sizeof( *m_puIdx[0] ) * unitScale[i].scaleArea( area.blocks[i].area() ) );
}
m_puCache.cache( pus );
m_numPUs = 0;
for( auto &pcu : cus )
{
pcu->firstPU = pcu->lastPU = nullptr;
}
}
void CodingStructure::clearCUs()
{
int numCh = ::getNumberValidChannels( area.chromaFormat );
for( int i = 0; i < numCh; i++ )
{
memset( m_cuIdx[i], 0, sizeof( *m_cuIdx[0] ) * unitScale[i].scaleArea( area.blocks[i].area() ) );
}
m_cuCache.cache( cus );
m_numCUs = 0;
}
unitScale[i].area:这个unitScale应该是当前单位缩放比例数(看上面setDecomp()函数,设置)
UnitScale( int sx, int sy ) : posx(sx), posy(sy), area(posx+posy) {}
此时{x = 8, y = 0, width = 8, height = 4},推测posx根据Y或Cb分量不同已经定好了
m_isDecomp:推测为Decompose的意思,默认设置为false。
后面都是初始化清零一些参数
推测这个函数作用是:当前CU已测试完,所以清理一些参数,以方便下一个CU测试。注意这里这个函数是被tempSubCS和bestSubCS分别调用的,所以应该就是方便之后CU写入数据。这块之后再看一遍