学习帧内算是比较简单的了,所以更要打好基础
versatile video coding,我一直手误打成VCC,这主要源于我一开始学习的时候的臭毛病,没懂,但是一直挂在嘴上,久而久之就一直说成VCC了,大家不要学我;
帧内预测是消除空间性的,编码又是对像素而言的,那么显然我们就要用各种方法去预测,去降低码率,有0~67种方法索引,0和1分别为planar和DC模式,2~67则为各种角度下的角度模式;
帧内预测角度有65种(非常多)(还有两个不是角度)
VTM新增了很多帧内技术,具体有跨分量线性预测(Corss-Component Linear Mode, CCLM)、位置自适应帧内联合预测(Position dependent intra prediciton Combination, PDPC)、帧内多行预测(Multiple reference line intra prediction, MRL)、帧内再划分预测(Intra sub-Partition prediction, ISP)等。
ISP:Intra Sub-Partitions,也就是帧内再划分,VTM新增的工具,它将亮度帧内预测块按照一个维度划分为2个或4个子块,其中所有子块共享同一种帧内预测模式。
ISP是否被允许或者如何划分参考下表
宽或高大于最大变换块尺寸的块也不进行子块划分
之前的帧内预测包括方向角度预测和平滑预测(DC和Planar)两类,VVC为提高预测精度,将HEVC帧内的33种角度预测模式扩展到65种,如图所示,在原来的每两个预测方向间再建立一个预测方向;
对于水平类模式(19-33),需要将左侧参考像素投影到上侧参考像素的左侧,以扩展上侧参考像素;
对于垂直类模式(34-49),需要将上侧参考像素投影到左侧参考像素的上方,以扩展左侧参考像素。
垂直模式(模式50)
水平模式(模式18)
左下角对角线模式和与其相邻的8个模式(模式2~10)
右上角对角线模式和与其相邻的8个模式(模式58~66)。
相比于只需要对方块进行帧内预测处理的HEVC,VVC需要处理长宽不等的矩形块。由于角度模式时只能使用左侧和上方的长为2h和2w的参考像素,长宽不等时帧内预测受到限制。针对矩形块帧内预测的特点,VVC中引入了如上图虚线指示的宽角度模式。
宽角度模式在原来的两个45°角上进行了延申,使得能够适应w和h不相等的状况;
对于宽大于高且比值大于一定程度的模式,需要将模式2附近(大于模式2)的一些类水平角度模式替换成为模式66附近(大于模式66)的一些类垂直角度模式(是因为此时对于模式2附近(大于模式2)存在块部分点无法索引到参考像素的情况,而模式66附近(大于模式66)仍有部分能够索引到参考像素)。
如下图所示,宽高比过大时mode2已经索引不到参考像素,而mode76这样的大于mode66的模式变得可以使用。
同理,对于高大于宽且比值大于一定程度的模式,需要将模式66附近(小于模式66)的一些类垂直角度模式替换成为模式2附近(小于模式2)的一些类水平角度模式(是因为此时对于模式66附近(小于模式66)存在部分点无法索引到参考像素的情况,而模式2附近(小于模式2)仍有部分能够索引到参考像素)。
代码用modeShift作为一个设定,用于调节触发宽角度模式的条件。当宽大于高时,宽高比越大modeShift越大,就会有更多的接近2的类垂直模式号进入宽角度模式转换模式号。当宽小于高时,宽高比越小modeShift越大,就会有更多接近66的类水平模式号进入宽角度模式转换模式号。
附上学习的代码:
// Function for initialization of intra prediction parameters 用于初始化内部预测参数的函数
void IntraPrediction::initPredIntraParams(const PredictionUnit & pu, const CompArea area, const SPS& sps) //传pu,area,sps信息
{
const ComponentID compId = area.compID;
const ChannelType chType = toChannelType(compId);
// 是否使用ISP(多划分预测)
const bool useISP = NOT_INTRA_SUBPARTITIONS != pu.cu->ispMode && isLuma(chType);
// 取size
const Size cuSize = Size(pu.cu->blocks[compId].width, pu.cu->blocks[compId].height);
const Size puSize = Size(area.width, area.height);
const Size &blockSize = useISP ? cuSize : puSize;
const int dirMode = PU::getFinalIntraMode(pu, chType); // 获取最后的帧内模式
const int predMode = getModifiedWideAngle(blockSize.width, blockSize.height, dirMode); //将预测模式转为宽角度模式,wide—angle
// ver-vertical 垂直 标号大于34视作垂直类模式
m_ipaParam.isModeVer = predMode >= DIA_IDX;
// 参数,参考行索引
m_ipaParam.multiRefIndex = isLuma(chType) ? pu.multiRefIdx : 0;
// 参考像素滤波初始化为否
m_ipaParam.refFilterFlag = false;
// 插值初始化为否
m_ipaParam.interpolationFlag = false;
// 是否提供PDPC技术(若PU边长均≥4且使用0号参考行则提供PDPC)
m_ipaParam.applyPDPC =
(puSize.width >= MIN_TB_SIZEY && puSize.height >= MIN_TB_SIZEY) && m_ipaParam.multiRefIndex == 0;
//记录角度模式关于垂直或水平的偏移量
//若predMode>=34,则intraPredAngLeMode=predMode-VER_IDX(50) VER_IDX = 50; HOR_IDX = 18
//若predMode<34,则intraPredAngLeMode=predMode-HOR_IDX(18)
//宽角度模式是-14~80,故该值范围为-32~30
// 是垂直类模式吗? 是:不是
const int intraPredAngleMode = (m_ipaParam.isModeVer) ? predMode - VER_IDX : -(predMode - HOR_IDX);
int absAng = 0;
//预测模式是角度模式(即模式号大于DC)且模式号小于LUMA模式总数67,planar:0 ,DC:1
if (dirMode > DC_IDX && dirMode < NUM_LUMA_MODE) // intraPredAngle for directional modes 用于定向模式的帧内角度预测
{ //偏离水平或垂直不同程度的不同模式对应的模式偏移值offset,应该是跟50,18相对应
static const int angTable[32] = { 0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 23, 26, 29, 32, 35, 39, 45, 51, 57, 64, 73, 86, 102, 128, 171, 256, 341, 512, 1024 };
// (512 * 32) / offset,跟上面的表格对应,为什么这么算还不清楚
static const int invAngTable[32] = {
0, 16384, 8192, 5461, 4096, 2731, 2048, 1638, 1365, 1170, 1024, 910, 819, 712, 630, 565,
512, 468, 420, 364, 321, 287, 256, 224, 191, 161, 128, 96, 64, 48, 32, 16
}; // (512 * 32) / Angle
//模式偏离水平或垂直的值(取绝对值),值范围为0~32
const int absAngMode = abs(intraPredAngleMode);
//模式19~49为-1,其他为1,也就是给垂直类和水平类一个符号
const int signAng = intraPredAngleMode < 0 ? -1 : 1;
//查找对应偏移值offset
absAng = angTable[absAngMode];
//查找表中角度偏移值的倒数*512*32,后续便于除法运算
m_ipaParam.absInvAngle = invAngTable[absAngMode];
// 帧内角度预测,两数相乘,此处就是给值附上“水平”或“垂直”
m_ipaParam.intraPredAngle = signAng * absAng;
//——————————————————————————————————————————————————
int IntraPrediction::getModifiedWideAngle( int width, int height, int predMode )
{
//The function returns a 'modified' wide angle index, given that it is not necessary
//in this software implementation to reserve the values 0 and 1 for Planar and DC to generate the prediction signal.
//It should only be used to obtain the intraPredAngle parameter.
//To simply obtain the wide angle index, the function PU::getWideAngle should be used instead.
// 如果没有必要,该函数返回一个“修改过的”广角索引
// 在本软件实现中,为Planar和DC保留0和1的值来产生预测信号。 它应该只用于获取intrapredangle参数。
// 为了简单地获得广角索引,应该使用函数PU::getWideAngle来代替。
// 66 是右上角45°位置的角度索引 2是左下角45°的角度索引
if ( predMode > DC_IDX && predMode <= VDIA_IDX ) // 判断条件,是否为角度模式?意思是0和1滚
{
int modeShift[] = { 0, 6, 10, 12, 14, 15 }; //这个表的建立应该是做这个的人测出来的,知道这么个表就ok;
// 计算意义上的宽高比
int deltaSize = abs(floorLog2(width) - floorLog2(height));
// 若宽大于高,且模式小于与宽高比对应的指定值,可以理解,如果比这个值还要大,那可能就不需要宽
if (width > height && predMode < 2 + modeShift[deltaSize]) //若宽大于高 且小于这个计算的值
{ //改变模式标号为原标号+65,对应图就可以理解
predMode += (VDIA_IDX - 1);
}
// 若高大于宽,且模式大于与宽高比对应的指定值
else if (height > width && predMode > VDIA_IDX - modeShift[deltaSize])
{
//若高大于宽,且模式大于与宽高比对应的指定值
predMode -= (VDIA_IDX - 1);
}
}
return predMode;
}