3.4 模式信息解码
作者:chai51
出处:https://www.cnblogs.com/chai51
版权:本文版权归作者和博客园共有
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
引言
模式信息解码(Mode Info Decoding)是AV1解码的关键步骤,它决定了每个块使用的预测方式、参考帧、运动向量等关键参数。模式信息解码根据帧类型(关键帧或帧间帧)和块类型(帧内块或帧间块)采用不同的解码流程,是连接块划分和预测解码的桥梁。
源码说明: 本文档基于作者自己编写的AV1解码器Python实现,所有代码示例和实现细节均来自实际可运行的源码。源码仓库:GitHub - av1_learning
模式信息解码概述
基本概念
模式信息解码是在块划分解码之后,预测解码之前进行的步骤,主要工作包括:
- 帧类型判断:根据帧类型选择不同的解码路径
- 块类型判断:在帧间帧中判断是帧内块还是帧间块
- 模式信息解码:解码预测模式、参考帧、运动向量等
- 参数初始化:初始化CDEF、量化、滤波等参数
解码路径
模式信息解码根据帧类型分为两条主要路径:
模式信息解码(__mode_info)
├─> 关键帧(FrameIsIntra)
│ └─> 帧内模式信息解码(__intra_frame_mode_info)
│ ├─> Segment ID解码
│ ├─> Skip标志解码
│ ├─> CDEF/量化/滤波参数
│ ├─> IntraBC模式检查
│ └─> 帧内预测模式解码
│
└─> 帧间帧(!FrameIsIntra)
└─> 帧间模式信息解码(__inter_frame_mode_info)
├─> Segment ID解码
├─> Skip Mode解码
├─> Skip标志解码
├─> CDEF/量化/滤波参数
├─> is_inter标志解码
└─> 根据is_inter选择:
├─> is_inter=0: 帧内块模式信息(__intra_block_mode_info)
└─> is_inter=1: 帧间块模式信息(__inter_block_mode_info)
模式信息解码主函数
位置: src/tile/tile_group.py - __mode_info()
规范文档: 5.11.6 Mode info syntax
核心实现
def __mode_info(self, av1: AV1Decoder):
"""
模式信息解析函数
规范文档 5.11.6 Mode info syntax
"""
frame_header = av1.frame_header
tile_group = self.tile_group
if frame_header.FrameIsIntra:
self.__intra_frame_mode_info(av1) # 关键帧路径
else:
self.__inter_frame_mode_info(av1) # 帧间帧路径
# 验证segment_id范围
assert tile_group.segment_id >= 0
assert tile_group.segment_id <= frame_header.LastActiveSegId
流程图
关键帧模式信息解码
位置: src/tile/tile_group.py - __intra_frame_mode_info()
规范文档: 5.11.7 Intra frame mode info syntax
解码流程
关键帧中所有块都使用帧内预测,模式信息解码流程如下:
def __intra_frame_mode_info(self, av1: AV1Decoder):
"""
解析帧内模式信息
规范文档 5.11.7 Intra frame mode info syntax
"""
frame_header = av1.frame_header
tile_group = self.tile_group
HasChroma = tile_group.HasChroma
# 1. 初始化skip标志
tile_group.skip = 0
# 2. Segment ID解码(如果SegIdPreSkip)
if frame_header.SegIdPreSkip:
self.__intra_segment_id(av1)
# 3. Skip标志解码
tile_group.skip_mode = 0
self.__read_skip(av1)
# 4. Segment ID解码(如果!SegIdPreSkip)
if not frame_header.SegIdPreSkip:
self.__intra_segment_id(av1)
# 5. CDEF/量化/滤波参数解码
self.__read_cdef(av1)
self.__read_delta_qindex(av1)
self.__read_delta_lf(av1)
# 6. 初始化参考帧
tile_group.ReadDeltas = 0
tile_group.RefFrame[0] = REF_FRAME.INTRA_FRAME
tile_group.RefFrame[1] = REF_FRAME.NONE
# 7. IntraBC模式检查
if frame_header.allow_intrabc:
tile_group.use_intrabc = read_S(av1, 'use_intrabc')
else:
tile_group.use_intrabc = 0
if tile_group.use_intrabc:
# IntraBC模式:使用当前帧作为参考的帧间模式
tile_group.is_inter = 1
tile_group.YMode = Y_MODE.DC_PRED
tile_group.UVMode = Y_MODE.DC_PRED
tile_group.motion_mode = MOTION_MODE.SIMPLE
tile_group.compound_type = COMPOUND_TYPE.COMPOUND_AVERAGE
tile_group.PaletteSizeY = 0
tile_group.PaletteSizeUV = 0
tile_group.interp_filter[0] = INTERPOLATION_FILTER.BILINEAR
tile_group.interp_filter[1] = INTERPOLATION_FILTER.BILINEAR
tile_group.RefStackMv = find_mv_stack(av1, 0)
self.__assign_mv(av1, 0)
else:
# 标准帧内模式
tile_group.is_inter = 0
# 8. 亮度模式解码
intra_frame_y_mode = Y_MODE(read_S(av1, 'intra_frame_y_mode'))
tile_group.YMode = intra_frame_y_mode
# 9. 亮度角度信息解码
self.__intra_angle_info_y(av1)
# 10. 色度模式解码(如果有色度)
if HasChroma:
uv_mode = Y_MODE(read_S(av1, 'uv_mode'))
tile_group.UVMode = uv_mode
if tile_group.UVMode == Y_MODE.UV_CFL_PRED:
self.__read_cfl_alphas(av1) # CFL alpha值
self.__intra_angle_info_uv(av1)
# 11. 调色板模式信息(如果允许)
tile_group.PaletteSizeY = 0
tile_group.PaletteSizeUV = 0
if (tile_group.MiSize >= SUB_SIZE.BLOCK_8X8 and
Block_Width[tile_group.MiSize] <= 64 and
Block_Height[tile_group.MiSize] <= 64 and
frame_header.allow_screen_content_tools):
self.__palette_mode_info(av1)
# 12. 滤波器内模式信息
self.__filter_intra_mode_info(av1)
关键步骤说明
- Segment ID解码:根据
SegIdPreSkip标志决定解码顺序 - Skip标志解码:关键帧中
skip_mode始终为0 - 参数解码:CDEF索引、量化参数增量、滤波参数增量
- IntraBC模式:如果启用,使用当前帧作为参考的帧间模式
- 帧内预测模式:亮度模式、角度信息、色度模式、调色板、滤波器内
帧间帧模式信息解码
位置: src/tile/tile_group.py - __inter_frame_mode_info()
规范文档: 5.11.18 Inter frame mode info syntax
解码流程
帧间帧中需要先判断块类型,然后选择相应的解码路径:
def __inter_frame_mode_info(self, av1: AV1Decoder):
"""
解析帧间模式信息
规范文档 5.11.18 Inter frame mode info syntax
"""
frame_header = av1.frame_header
tile_group = self.tile_group
MiRow = tile_group.MiRow
MiCol = tile_group.MiCol
AvailU = tile_group.AvailU
AvailL = tile_group.AvailL
# 1. 初始化IntraBC
tile_group.use_intrabc = 0
# 2. 获取相邻块的参考帧信息
tile_group.LeftRefFrame[0] = tile_group.RefFrames[MiRow][MiCol - 1][0] if AvailL else REF_FRAME.INTRA_FRAME
tile_group.LeftRefFrame[1] = tile_group.RefFrames[MiRow][MiCol - 1][1] if AvailL else REF_FRAME.NONE
tile_group.AboveRefFrame[0] = tile_group.RefFrames[MiRow - 1][MiCol][0] if AvailU else REF_FRAME.INTRA_FRAME
tile_group.AboveRefFrame[1] = tile_group.RefFrames[MiRow - 1][MiCol][1] if AvailU else REF_FRAME.NONE
# 3. 计算相邻块的类型
tile_group.LeftIntra = tile_group.LeftRefFrame[0] <= REF_FRAME.INTRA_FRAME
tile_group.AboveIntra = tile_group.AboveRefFrame[0] <= REF_FRAME.INTRA_FRAME
tile_group.LeftSingle = tile_group.LeftRefFrame[1] <= REF_FRAME.INTRA_FRAME
tile_group.AboveSingle = tile_group.AboveRefFrame[1] <= REF_FRAME.INTRA_FRAME
# 4. 初始化skip标志
tile_group.skip = 0
# 5. Segment ID解码
self.__inter_segment_id(av1, 1)
# 6. Skip Mode解码
self.__read_skip_mode(av1)
# 7. Skip标志解码
if tile_group.skip_mode:
tile_group.skip = 1
else:
self.__read_skip(av1)
# 8. Segment ID解码(如果!SegIdPreSkip)
if not frame_header.SegIdPreSkip:
self.__inter_segment_id(av1, 0)
# 9. 计算Lossless标志
tile_group.Lossless = frame_header.LosslessArray[tile_group.segment_id]
# 10. CDEF/量化/滤波参数解码
self.__read_cdef(av1)
self.__read_delta_qindex(av1)
self.__read_delta_lf(av1)
tile_group.ReadDeltas = 0
# 11. is_inter标志解码
self.__read_is_inter(av1)
# 12. 根据is_inter选择解码路径
if tile_group.is_inter:
self.__inter_block_mode_info(av1) # 帧间块
else:
self.__intra_block_mode_info(av1) # 帧内块
is_inter标志解码
位置: src/tile/tile_group.py - __read_is_inter()
def __read_is_inter(self, av1: AV1Decoder):
"""
读取is_inter标志
规范文档 5.11.20 Is inter syntax
"""
frame_header = av1.frame_header
tile_group = self.tile_group
if tile_group.skip_mode:
tile_group.is_inter = 1 # Skip Mode强制为帧间
elif self.__seg_feature_active(av1, SEG_LVL_REF_FRAME):
# Segment级别参考帧特征
tile_group.is_inter = frame_header.FeatureData[
tile_group.segment_id][SEG_LVL_REF_FRAME] != REF_FRAME.INTRA_FRAME
elif self.__seg_feature_active(av1, SEG_LVL_GLOBALMV):
tile_group.is_inter = 1 # Global MV强制为帧间
else:
tile_group.is_inter = read_S(av1, 'is_inter') # 从比特流解码
帧内块模式信息解码
位置: src/tile/tile_group.py - __intra_block_mode_info()
规范文档: 5.11.22 Intra block mode info syntax
解码流程
帧间帧中的帧内块模式信息解码与关键帧类似,但更简化:
def __intra_block_mode_info(self, av1: AV1Decoder):
"""
解析帧内块模式信息(在帧间帧中的帧内块)
规范文档 5.11.22 Intra block mode info syntax
"""
frame_header = av1.frame_header
tile_group = self.tile_group
MiSize = tile_group.MiSize
HasChroma = tile_group.HasChroma
# 1. 设置参考帧
tile_group.RefFrame[0] = REF_FRAME.INTRA_FRAME
tile_group.RefFrame[1] = REF_FRAME.NONE
# 2. 亮度模式解码
y_mode = Y_MODE(read_S(av1, 'y_mode'))
tile_group.YMode = y_mode
# 3. 亮度角度信息解码
self.__intra_angle_info_y(av1)
# 4. 色度模式解码(如果有色度)
if HasChroma:
uv_mode = Y_MODE(read_S(av1, 'uv_mode'))
tile_group.UVMode = uv_mode
if tile_group.UVMode == Y_MODE.UV_CFL_PRED:
self.__read_cfl_alphas(av1)
self.__intra_angle_info_uv(av1)
# 5. 调色板模式信息(如果允许)
tile_group.PaletteSizeY = 0
tile_group.PaletteSizeUV = 0
if (MiSize >= SUB_SIZE.BLOCK_8X8 and
Block_Width[MiSize] <= 64 and
Block_Height[MiSize] <= 64 and
frame_header.allow_screen_content_tools):
self.__palette_mode_info(av1)
# 6. 滤波器内模式信息
self.__filter_intra_mode_info(av1)
与关键帧的区别
- 参考帧设置:直接设置为
INTRA_FRAME,无需检查 - 符号名称:使用
y_mode而不是intra_frame_y_mode - 简化流程:不需要Segment ID、Skip、CDEF等参数(已在帧间模式信息中解码)
帧间块模式信息解码
位置: src/tile/tile_group.py - __inter_block_mode_info()
规范文档: 5.11.23 Inter block mode info syntax
解码流程
帧间块模式信息解码是帧间帧的核心,包括参考帧、运动向量、运动模式等:
def __inter_block_mode_info(self, av1: AV1Decoder):
"""
解析帧间块模式信息
规范文档 5.11.23 Inter block mode info syntax
"""
seq_header = av1.seq_header
frame_header = av1.frame_header
tile_group = self.tile_group
# 1. 初始化调色板
tile_group.PaletteSizeY = 0
tile_group.PaletteSizeUV = 0
# 2. 参考帧解码
self.__read_ref_frames(av1)
# 3. 判断是否为复合预测
isCompound = (tile_group.RefFrame[1] > REF_FRAME.INTRA_FRAME)
# 4. 构建MV栈
tile_group.RefStackMv = find_mv_stack(av1, isCompound)
# 5. Y模式解码
if tile_group.skip_mode:
tile_group.YMode = Y_MODE.NEAREST_NEARESTMV
elif (self.__seg_feature_active(av1, SEG_LVL_SKIP) or
self.__seg_feature_active(av1, SEG_LVL_GLOBALMV)):
tile_group.YMode = Y_MODE.GLOBALMV
elif isCompound:
compound_mode = read_S(av1, 'compound_mode')
tile_group.YMode = Y_MODE(Y_MODE.NEAREST_NEARESTMV + compound_mode)
else:
new_mv = read_S(av1, 'new_mv')
if new_mv == 0:
tile_group.YMode = Y_MODE.NEWMV
else:
zero_mv = read_S(av1, "zero_mv")
if zero_mv == 0:
tile_group.YMode = Y_MODE.GLOBALMV
else:
ref_mv = read_S(av1, 'ref_mv')
tile_group.YMode = Y_MODE.NEARESTMV if ref_mv == 0 else Y_MODE.NEARMV
# 6. RefMvIdx解码
tile_group.RefMvIdx = 0
if tile_group.YMode in [Y_MODE.NEWMV, Y_MODE.NEW_NEWMV]:
# NEWMV模式:从MV栈中选择
for idx in range(2):
if tile_group.NumMvFound > idx + 1:
drl_mode = read_S(av1, "drl_mode", idx=idx)
if drl_mode == 0:
tile_group.RefMvIdx = idx
break
tile_group.RefMvIdx = idx + 1
elif has_nearmv():
# NEARMV模式:从MV栈中选择
tile_group.RefMvIdx = 1
for idx in range(1, 3):
if tile_group.NumMvFound > idx + 1:
drl_mode = read_S(av1, "drl_mode", idx=idx)
if drl_mode == 0:
tile_group.RefMvIdx = idx
break
tile_group.RefMvIdx = idx + 1
# 7. 运动向量分配
self.__assign_mv(av1, isCompound)
# 8. 插值滤波器解码
if needs_interp_filter():
for dir in range(1 + isCompound):
tile_group.interp_filter[dir] = INTERPOLATION_FILTER(read_S(av1, 'interp_filter', dir=dir))
else:
for dir in range(1 + isCompound):
tile_group.interp_filter[dir] = INTERPOLATION_FILTER.BILINEAR
# 9. 复合类型解码(如果是复合预测)
if isCompound:
self.__read_compound_type(av1)
# 10. Inter-Intra模式解码
if not isCompound and not tile_group.skip_mode:
self.__read_interintra_mode(av1, isCompound)
# 11. 运动模式解码
if not tile_group.skip_mode:
self.__read_motion_mode(av1, isCompound)
关键步骤说明
- 参考帧解码:解码参考帧索引,判断是否为复合预测
- MV栈构建:构建运动向量候选栈
- Y模式解码:根据Skip Mode、Segment特征、复合预测等选择Y模式
- RefMvIdx解码:从MV栈中选择参考运动向量
- 运动向量分配:根据Y模式分配运动向量
- 插值滤波器:解码子像素插值滤波器类型
- 复合类型:复合预测的混合方式
- Inter-Intra模式:帧间-帧内混合模式
- 运动模式:SIMPLE、OBMC、LOCALWARP
模式信息解码流程图
关键数据结构
TileGroup中的模式信息字段
class TileGroup:
# 块位置和尺寸
MiRow: int # MI行位置
MiCol: int # MI列位置
MiSize: SUB_SIZE # 块尺寸
# 预测类型
is_inter: int # 是否为帧间预测
skip: int # Skip标志
skip_mode: int # Skip Mode标志
# 帧内预测模式
YMode: Y_MODE # 亮度预测模式
UVMode: Y_MODE # 色度预测模式
AngleDeltaY: int # 亮度角度增量
AngleDeltaUV: int # 色度角度增量
use_filter_intra: int # 是否使用滤波器内
filter_intra_mode: int # 滤波器内模式
# 帧间预测模式
RefFrame: List[REF_FRAME] # 参考帧索引
YMode: Y_MODE # Y模式(帧间模式)
RefMvIdx: int # 参考MV索引
Mv: List[MotionVector] # 运动向量
motion_mode: int # 运动模式
interp_filter: List # 插值滤波器
compound_type: int # 复合类型
interintra: int # Inter-Intra标志
interintra_mode: int # Inter-Intra模式
# 其他参数
segment_id: int # Segment ID
Lossless: int # 是否无损
PaletteSizeY: int # 亮度调色板大小
PaletteSizeUV: int # 色度调色板大小
与解码流程的集成
模式信息解码在解码流程中的位置:
块划分解码(__decode_partition)
→ 解码块(__decode_block)
→ 模式信息解码(__mode_info)
→ 根据帧类型选择路径
→ 关键帧:帧内模式信息
→ 帧间帧:帧间模式信息
→ 根据is_inter选择
→ 帧内块:帧内块模式信息
→ 帧间块:帧间块模式信息
→ 调色板语法解码(__palette_tokens)
→ 变换尺寸解码(__read_block_tx_size)
→ 计算预测(__compute_prediction)
→ 残差解码(__residual)
总结
模式信息解码是AV1解码的核心环节,主要特点包括:
- 路径选择:根据帧类型和块类型选择不同的解码路径
- 参数解码:解码预测模式、参考帧、运动向量等关键参数
- 上下文依赖:利用相邻块的信息进行上下文自适应解码
- 灵活性:支持多种预测模式和组合方式
模式信息解码为后续的预测解码和残差解码提供了必要的参数,是AV1实现高效压缩的重要技术。
参考资源:
- AV1规范文档
- 源码实现: GitHub - av1_learning
- 模式信息解码:
src/tile/tile_group.py-__mode_info() - 帧内模式信息:
src/tile/tile_group.py-__intra_frame_mode_info() - 帧间模式信息:
src/tile/tile_group.py-__inter_frame_mode_info()
- 模式信息解码:

浙公网安备 33010602011771号