3.4 模式信息解码

作者:chai51
出处:https://www.cnblogs.com/chai51
版权:本文版权归作者和博客园共有
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任

引言

模式信息解码(Mode Info Decoding)是AV1解码的关键步骤,它决定了每个块使用的预测方式、参考帧、运动向量等关键参数。模式信息解码根据帧类型(关键帧或帧间帧)和块类型(帧内块或帧间块)采用不同的解码流程,是连接块划分和预测解码的桥梁。

源码说明: 本文档基于作者自己编写的AV1解码器Python实现,所有代码示例和实现细节均来自实际可运行的源码。源码仓库:GitHub - av1_learning


模式信息解码概述

基本概念

模式信息解码是在块划分解码之后,预测解码之前进行的步骤,主要工作包括:

  1. 帧类型判断:根据帧类型选择不同的解码路径
  2. 块类型判断:在帧间帧中判断是帧内块还是帧间块
  3. 模式信息解码:解码预测模式、参考帧、运动向量等
  4. 参数初始化:初始化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

流程图

graph TD A[开始模式信息解码<br/>__mode_info] --> B{帧类型检查<br/>FrameIsIntra?} B -->|是| C[关键帧路径<br/>__intra_frame_mode_info] B -->|否| D[帧间帧路径<br/>__inter_frame_mode_info] C --> E[Segment ID解码] E --> F[Skip标志解码] F --> G[CDEF/量化/滤波参数] G --> H{IntraBC模式?} H -->|是| I[IntraBC模式处理] H -->|否| J[帧内预测模式解码] I --> K[模式信息解码完成] J --> K D --> L[Segment ID解码] L --> M[Skip Mode解码] M --> N[Skip标志解码] N --> O[CDEF/量化/滤波参数] O --> P[is_inter标志解码] P --> Q{is_inter?} Q -->|0| R[帧内块模式信息<br/>__intra_block_mode_info] Q -->|1| S[帧间块模式信息<br/>__inter_block_mode_info] R --> K S --> K style A fill:#e1f5ff style B fill:#fff9c4 style Q fill:#fff9c4 style K fill:#c8e6c9

关键帧模式信息解码

位置: 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)

关键步骤说明

  1. Segment ID解码:根据SegIdPreSkip标志决定解码顺序
  2. Skip标志解码:关键帧中skip_mode始终为0
  3. 参数解码:CDEF索引、量化参数增量、滤波参数增量
  4. IntraBC模式:如果启用,使用当前帧作为参考的帧间模式
  5. 帧内预测模式:亮度模式、角度信息、色度模式、调色板、滤波器内

帧间帧模式信息解码

位置: 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)

与关键帧的区别

  1. 参考帧设置:直接设置为INTRA_FRAME,无需检查
  2. 符号名称:使用y_mode而不是intra_frame_y_mode
  3. 简化流程:不需要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)

关键步骤说明

  1. 参考帧解码:解码参考帧索引,判断是否为复合预测
  2. MV栈构建:构建运动向量候选栈
  3. Y模式解码:根据Skip Mode、Segment特征、复合预测等选择Y模式
  4. RefMvIdx解码:从MV栈中选择参考运动向量
  5. 运动向量分配:根据Y模式分配运动向量
  6. 插值滤波器:解码子像素插值滤波器类型
  7. 复合类型:复合预测的混合方式
  8. Inter-Intra模式:帧间-帧内混合模式
  9. 运动模式:SIMPLE、OBMC、LOCALWARP

模式信息解码流程图

graph TD A[开始模式信息解码] --> B{FrameIsIntra?} B -->|是| C[关键帧路径] C --> C1[Segment ID] C1 --> C2[Skip标志] C2 --> C3[CDEF/量化/滤波] C3 --> C4{IntraBC?} C4 -->|是| C5[IntraBC模式] C4 -->|否| C6[帧内预测模式] C5 --> Z[完成] C6 --> Z B -->|否| D[帧间帧路径] D --> D1[获取相邻块参考帧] D1 --> D2[Segment ID] D2 --> D3[Skip Mode] D3 --> D4[Skip标志] D4 --> D5[CDEF/量化/滤波] D5 --> D6[is_inter标志] D6 --> D7{is_inter?} D7 -->|0| E[帧内块模式] E --> E1[设置RefFrame] E1 --> E2[Y模式] E2 --> E3[角度信息] E3 --> E4[UV模式] E4 --> E5[调色板/滤波器内] E5 --> Z D7 -->|1| F[帧间块模式] F --> F1[参考帧解码] F1 --> F2[构建MV栈] F2 --> F3[Y模式解码] F3 --> F4[RefMvIdx] F4 --> F5[运动向量分配] F5 --> F6[插值滤波器] F6 --> F7{isCompound?} F7 -->|是| F8[复合类型] F7 -->|否| F9[Inter-Intra模式] F8 --> F10[运动模式] F9 --> F10 F10 --> Z style A fill:#e1f5ff style B fill:#fff9c4 style D7 fill:#fff9c4 style Z fill:#c8e6c9

关键数据结构

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解码的核心环节,主要特点包括:

  1. 路径选择:根据帧类型和块类型选择不同的解码路径
  2. 参数解码:解码预测模式、参考帧、运动向量等关键参数
  3. 上下文依赖:利用相邻块的信息进行上下文自适应解码
  4. 灵活性:支持多种预测模式和组合方式

模式信息解码为后续的预测解码和残差解码提供了必要的参数,是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()

上一篇: 块划分
下一篇: 残差解码

posted @ 2026-01-24 09:38  chai51  阅读(1)  评论(0)    收藏  举报