帧内块拷贝 (Intra block copy, IBC) 是 HEVC 针对屏幕内容编码(Screen content coding)序列的扩展工具,它显着提高了屏幕内容序列的编码效率。
IBC 是一种块级编码模式, IBC 编码的 CU 被视为除帧内或帧间预测模式之外的第三预测模式。和帧间技术类似,编码端执行运动搜索(块匹配,Block Maching, BM )为每个 CU 找到其最佳的块向量(Block Vector,也可以称为运动向量 Motion Vector),块向量用于指示从当前块到参考块的位移。和帧间技术不同的地方在于,IBC 的最佳块向量是在当前 CU所处帧重建区域搜索得到的,而帧间的运动向量是通过相邻参考帧内搜索得到的。
IBC 编码的 CU 的亮度块向量是整数精度。 色度块向量也四舍五入到整数精度。 当 IBC 与 AMVR 结合使用时,IBC 模式可以在 1 像素和 4 像素块向量精度之间切换。
IBC应用条件:IBC 模式适用于宽度和高度均小于或等于 64 的 CU。当 CU 为DUAL_TREE partition时,禁止色度块应用IBC。
和帧间技术类似,IBC也包括 IBC Merge模式和 IBC AMVP模式,编码端通过一个flag来标识 IBC AMVP 模式或 IBC Skip/Merge模式,如下所示:
- IBC Skip/Merge 模式:Merge 候选索引用于指示来自 IBC 编码块的相邻候选列表中的哪些块向量用于预测当前块。 Merge列表由空间、HMVP 和成对候选者组成。
- IBC AMVP 模式:块向量差以与运动向量差相同的方式编码。 块向量预测方法使用两个候选作为预测变量,一个来自左邻居,一个来自上邻居(如果 IBC 编码)。 当任一邻居不可用时,将使用默认块向量作为预测向量。 需要发送信号以指示块预测矢量的索引。
对于IBC AMVP模式,需要通过运动估计来搜索最佳块运动向量。IBC的运动估计方法包括基于哈希的运动估计(hash-based search)和基于块匹配的运动搜索(block matching based local search)。对于AMVP模式,首先使用基于哈希的搜索来执行块向量搜索。 如果哈希搜索没有返回有效的候选者,则执行基于块匹配的本地搜索。
在基于哈希(hash-based)的搜索中,当前块和参考块之间的散列键匹配(32 位 CRC)被扩展到所有允许的块大小。 当前图片中每个位置的哈希键计算基于 4x4 子块。 对于更大尺寸的当前块,当所有4×4子块的哈希键与对应参考位置的哈希键匹配时,确定哈希键与参考块的哈希键匹配。 如果发现多个参考块的哈希键与当前块的哈希键匹配,则计算每个匹配参考的块向量成本,并选择成本最小的一个。
一、IBC 参考区域
为了减少内存消耗和解码器复杂度,VVC 中的 IBC 只允许预定义区域(同一CTU Line)的重建部分,包括当前 CTU 的区域和左侧 CTU 的某些区域。 下图说明了 IBC 模式的参考区域,其中每个块代表 64x64 亮度样本。粗实线代表一个CTU,一个CTU包括 4 个64x64的亮度块。
根据当前CU在CTU中的位置可分为下面4种情况:
- 如果当前块落入当前CTU左上角的64x64块中,如左上图,那么除了当前CTU中已重建的样本外,还可以参考CPR模式下左侧CTU的右下角64x64的块、左下角64x64的块、右上角64x64的块。
- 如果当前CU是CTU右上角的64x64的块,如右上图,除了当前CTU已重建的样本,如果相对于当前CTU的(0,64)位置还未重建,当前块也能参考CPR模式下左侧CTU的右下角64x64的块、左下角64x64的块;否则当前块能参考左侧CTU的右下角64x64的块
- 如果当前CU是CTU左下角的64x64的块,如左下图,除了当前CTU已重建部分,如果相对于当前CTU的(64, 0) 位置还未重建,当前块也能参考CPR模式下当前CTU的右上角64x64的块、左侧CTU的右下角64x64的块;否则当前块能参考CPR模式下左侧CTU的右下角64x64的块。
- 如果当前CU是CTU右下角的64x64的块,它只能参考CPR模式下当前CTU已重建部分。
判断其在参考块是否有效:searchBv 函数
bool InterSearch::searchBv(PredictionUnit& pu, int xPos, int yPos, int width, int height, int picWidth, int picHeight, int xBv, int yBv, int ctuSize)
{
const int ctuSizeLog2 = floorLog2(ctuSize);
int refRightX = xPos + xBv + width - 1; // 参考块的右上角的坐标
int refBottomY = yPos + yBv + height - 1; // 参考块的左下角的坐标
// 参考块左上角的坐标 (x, y)
int refLeftX = xPos + xBv;
int refTopY = yPos + yBv;
if ((xPos + xBv) < 0)
{
return false;
}
if (refRightX >= picWidth) // 判断右侧是否超出图像边界
{
return false;
}
if ((yPos + yBv) < 0)
{
return false;
}
if (refBottomY >= picHeight) // 判断下侧是否超出图像边界
{
return false;
}
if ((xBv + width) > 0 && (yBv + height) > 0)
{
return false;
}
// Don't search the above CTU row 不要搜索上面的 CTU 行
if (refTopY >> ctuSizeLog2 < yPos >> ctuSizeLog2)
return false;
// Don't search the below CTU row 不要搜索下面的 CTU 行
if (refBottomY >> ctuSizeLog2 > yPos >> ctuSizeLog2)
{
return false;
}
// 判断参考块和当前块是否在同一个Tile中
unsigned curTileIdx = pu.cs->pps->getTileIdx(pu.lumaPos());
unsigned refTileIdx = pu.cs->pps->getTileIdx(Position(refLeftX, refTopY));
if (curTileIdx != refTileIdx)
{
return false;
}
refTileIdx = pu.cs->pps->getTileIdx(Position(refLeftX, refBottomY));
if (curTileIdx != refTileIdx)
{
return false;
}
refTileIdx = pu.cs->pps->getTileIdx(Position(refRightX, refTopY));
if (curTileIdx != refTileIdx)
{
return false;
}
refTileIdx = pu.cs->pps->getTileIdx(Position(refRightX, refBottomY));
if (curTileIdx != refTileIdx)
{
return false;
}
// in the same CTU line 同一CTU行
int numLeftCTUs = (1 << ((7 - ctuSizeLog2) << 1)) - ((ctuSizeLog2 < 7) ? 1 : 0);
// refRightX >> ctuSizeLog2 <= xPos >> ctuSizeLog2 应该是判断参考区域所在CTU在当前CTU或者左侧相邻CTU
// (refLeftX >> ctuSizeLog2 >= (xPos >> ctuSizeLog2) - numLeftCTUs)) 应该是判断参考区域所在CTU不能超出左侧相邻CTU
if ((refRightX >> ctuSizeLog2 <= xPos >> ctuSizeLog2) && (refLeftX >> ctuSizeLog2 >= (xPos >> ctuSizeLog2) - numLeftCTUs))
{
// in the same CTU, or left CTU 参考块在同一个CTU或者左侧相邻CTU
// if part of ref block is in the left CTU, some area can be referred from the not-yet updated local CTU buffer
// 如果参考块的一部分在左侧 CTU 中,则可以从尚未更新的本地 CTU 缓冲区中引用某些区域
if (((refLeftX >> ctuSizeLog2) == ((xPos >> ctuSizeLog2) - 1)) && (ctuSizeLog2 == 7))
{
// ref block's collocated block in current CTU 当前 CTU 中参考块的并置块
const Position refPosCol = pu.Y().topLeft().offset(xBv + ctuSize, yBv);
int offset64x = (refPosCol.x >> (ctuSizeLog2 - 1)) << (ctuSizeLog2 - 1);
int offset64y = (refPosCol.y >> (ctuSizeLog2 - 1)) << (ctuSizeLog2 - 1);
const Position refPosCol64x64 = {offset64x, offset64y};
if (pu.cs->isDecomp(refPosCol64x64, toChannelType(COMPONENT_Y)))
return false;
if (refPosCol64x64 == pu.Y().topLeft())
return false;
}
}
else
return false;
// in the same CTU, or valid area from left CTU. Check if the reference block is already coded
// 在同一个 CTU 或左侧 CTU 的有效区域中。 检查参考块是否已经重建
const Position refPosLT = pu.Y().topLeft().offset(xBv, yBv); // 参考区域的左上角
const Position refPosBR = pu.Y().bottomRight().offset(xBv, yBv); // 参考区域的右上角
const ChannelType chType = toChannelType(COMPONENT_Y);
if (!pu.cs->isDecomp(refPosBR, chType))
return false;
if (!pu.cs->isDecomp(refPosLT, chType))
return false;
return true;
}
二、IBC 与其他编码工具的交互
IBC 模式与 VVC 中其他帧间编码工具之间的交互,例如成对 Merge 候选、基于历史的运动矢量预测 (HMVP)、帧内/帧间联合预测 (CIIP)、具有运动矢量差的 Merge 模式 (MMVD) 和几何划分模式(GPM)如下:
- IBC 可以与成对 Merge 候选以及 HMVP 一起使用。 可以通过平均两个 IBC Merge候选来生成新的成对 IBC 合并候选。 对于 HMVP,IBC 运动信息被插入历史缓冲区以供将来参考。
- IBC 不能与以下工具结合使用:Affine、CIIP、MMVD 和 GPM。
与HEVC屏幕内容编码扩展不同,当前图片不再作为参考图片之一包含在IBC预测的参考图片列表0中。文章来源:https://www.toymoban.com/news/detail-421773.html
与 HEVC 屏幕内容编码扩展不同,当前图片不再作为参考图片之一包含在用于 IBC 预测的参考图片列表 0 中。 IBC 模式的运动矢量推导过程排除了帧间模式中的所有相邻块,反之亦然。VVC的IBC包括了以下改进:文章来源地址https://www.toymoban.com/news/detail-421773.html
- IBC 与常规 MV Merge 共享相同的过程,包括成对 Merge 候选和基于历史的运动预测,但不允许 TMVP 和零向量,因为它们对 IBC 模式无效。
- 单独的 HMVP 缓冲区(每个 5 个候选)分别用于传统的 MV 和 IBC。
- 块向量约束以比特流一致性约束的形式实现,编码器需要确保比特流中不存在无效向量,如果Merge 候选无效(超出范围或为0),则不应使用 Merge 。这种比特流一致性约束用虚拟缓冲器来表达。
- 对于Deblock,IBC 作为帧间模式处理。
- 如果当前块使用IBC预测模式编码,AMVR不使用四分之一像素;相反,AMVR 仅指示 MV 是整数像素还是 4 整数像素。
- IBC Merge 候选者的数量可以在 Slice header 中与常规、子块和几何 Merge 候选者的数量分开表示。
到了这里,关于H.266/VVC SCC技术学习:帧内块拷贝(Intra block copy, IBC)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!