3.2 Superblock解码

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

引言

Superblock(超级块)是AV1解码的基本处理单元,每个Superblock可以独立解码,是Tile解码的核心。Superblock解码包括上下文初始化、参数读取和块划分解码等步骤,为后续的块级解码奠定基础。

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


Superblock概述

Superblock的基本概念

Superblock是AV1的基本编码单元,具有以下特点:

  1. 尺寸:可以是64x64或128x128像素(由序列头中的use_128x128_superblock决定)
  2. 独立性:每个Superblock可以独立解码
  3. 边界对齐:Superblock边界必须对齐到4x4块边界
  4. 递归划分:Superblock可以递归划分为更小的块

Superblock与Tile的关系

  • Tile:由多个Superblock组成的矩形区域
  • Superblock:Tile内的基本处理单元
  • Tile边界必须对齐到Superblock边界,确保解码的独立性

Superblock尺寸计算

Superblock的尺寸由序列头中的use_128x128_superblock标志决定:

use_128x128_superblock = seq_header.use_128x128_superblock
sbSize = SUB_SIZE.BLOCK_128X128 if use_128x128_superblock else SUB_SIZE.BLOCK_64X64
sbSize4 = Num_4x4_Blocks_Wide[sbSize]  # 转换为4x4块单位
  • 如果use_128x128_superblock == 1:Superblock为128x128像素(32x32个4x4块)
  • 如果use_128x128_superblock == 0:Superblock为64x64像素(16x16个4x4块)

Superblock解码流程

位置: src/tile/tile_group.py - __decode_tile()

规范文档: 5.11.2 Decode tile syntax

主要步骤

Superblock解码在Tile解码过程中进行,主要流程如下:

def __decode_tile(self, av1: AV1Decoder):
    """
    规范文档 5.11.2 Decode tile syntax
    """
    seq_header = av1.seq_header
    frame_header = av1.frame_header
    tile_group = self.tile_group
    MiRows = frame_header.MiRows
    MiCols = frame_header.MiCols
    use_128x128_superblock = seq_header.use_128x128_superblock
    NumPlanes = seq_header.color_config.NumPlanes

    # 1. 初始化上下文和参数
    clear_above_context(av1)
    tile_group.DeltaLF = [0] * FRAME_LF_COUNT
    for plane in range(NumPlanes):
        for pass_val in range(2):
            tile_group.RefSgrXqd[plane][pass_val] = Sgrproj_Xqd_Mid[pass_val]
            for i in range(WIENER_COEFFS):
                tile_group.RefLrWiener[plane][pass_val][i] = Wiener_Taps_Mid[i]

    # 2. 计算Superblock尺寸
    sbSize = SUB_SIZE.BLOCK_128X128 if use_128x128_superblock else SUB_SIZE.BLOCK_64X64
    sbSize4 = Num_4x4_Blocks_Wide[sbSize]

    # 3. 初始化CDEF索引数组
    tile_group.cdef_idx = Array(
        tile_group.cdef_idx, (MiRows + 32, MiCols + 32))

    # 4. 遍历每个Superblock
    for r in range(tile_group.MiRowStart, tile_group.MiRowEnd, sbSize4):
        clear_left_context(av1)  # 清除左侧上下文
        for c in range(tile_group.MiColStart, tile_group.MiColEnd, sbSize4):
            tile_group.ReadDeltas = frame_header.delta_q_present
            
            # 5. Superblock级别的处理
            self.__clear_cdef(av1, r, c)                    # 清除CDEF上下文
            self.__clear_block_decoded_flags(av1, r, c, sbSize4)  # 清除块解码标志
            self.__read_lr(av1, r, c, sbSize)              # 读取Loop Restoration参数
            self.__decode_partition(av1, r, c, sbSize)      # 解码块划分

Superblock解码流程图

graph TD A[开始Tile解码] --> B[初始化上下文和参数] B --> C[计算Superblock尺寸<br/>sbSize sbSize4] C --> E[遍历Superblock行<br/>r从MiRowStart到MiRowEnd] E --> F[清除左侧上下文<br/>clear_left_context] F --> G[遍历Superblock列<br/>c从MiColStart到MiColEnd] G --> H[设置ReadDeltas标志] H --> I[清除CDEF上下文<br/>__clear_cdef] I --> J[清除块解码标志<br/>__clear_block_decoded_flags] J --> K[读取Loop Restoration参数<br/>__read_lr] K --> L[解码块划分<br/>__decode_partition] L --> M{还有Superblock?} M -->|是| G M -->|否| N{还有行?} N -->|是| E N -->|否| O[Superblock解码完成] style A fill:#e1f5ff style L fill:#fff9c4 style O fill:#c8e6c9

详细技术文档参考

说明: 以下内容来自详细技术文档,包含完整的实现细节和代码说明。这些内容主要用于技术参考。

点击展开查看详细技术文档

1. 上下文初始化

在开始解码Superblock之前,需要初始化各种上下文和参数:

清除上方上下文

位置: src/tile/tile_group.py - clear_above_context()

规范文档: 6.10.2 clear_above_context()

clear_above_context(av1)

清除上方块的上下文信息,为新的Tile行做准备。

初始化Loop Restoration参数

tile_group.DeltaLF = [0] * FRAME_LF_COUNT
for plane in range(NumPlanes):
    for pass_val in range(2):
        tile_group.RefSgrXqd[plane][pass_val] = Sgrproj_Xqd_Mid[pass_val]
        for i in range(WIENER_COEFFS):
            tile_group.RefLrWiener[plane][pass_val][i] = Wiener_Taps_Mid[i]

初始化Loop Restoration的参考参数,包括SGR投影参数和Wiener滤波参数。

2. 清除CDEF上下文

位置: src/tile/tile_group.py - __clear_cdef()

规范文档: 5.11.55 Clear CDEF function

def __clear_cdef(self, av1: AV1Decoder, r: int, c: int):
    """
    清除CDEF上下文
    规范文档 5.11.55 Clear CDEF function
    """
    seq_header = av1.seq_header
    frame_header = av1.frame_header
    tile_group = self.tile_group
    use_128x128_superblock = seq_header.use_128x128_superblock

    tile_group.cdef_idx[r][c] = -1
    if use_128x128_superblock:
        cdefSize4 = Num_4x4_Blocks_Wide[SUB_SIZE.BLOCK_64X64]
        tile_group.cdef_idx[r][c + cdefSize4] = -1
        tile_group.cdef_idx[r + cdefSize4][c] = -1
        tile_group.cdef_idx[r + cdefSize4][c + cdefSize4] = -1

清除CDEF(Constrained Directional Enhancement Filter)索引,为后续的CDEF参数解码做准备。

  • 对于64x64 Superblock:清除1个CDEF索引
  • 对于128x128 Superblock:清除4个CDEF索引(每个64x64块一个)

3. 清除块解码标志

位置: src/tile/tile_group.py - __clear_block_decoded_flags()

规范文档: 5.11.3 Clear block decoded flags function

def __clear_block_decoded_flags(self, av1: AV1Decoder, r: int, c: int, sbSize4: int):
    """
    清除块解码标志
    规范文档 5.11.3 Clear block decoded flags function
    """
    seq_header = av1.seq_header
    tile_group = self.tile_group
    subsampling_x = seq_header.color_config.subsampling_x
    subsampling_y = seq_header.color_config.subsampling_y
    NumPlanes = seq_header.color_config.NumPlanes

    for plane in range(NumPlanes):
        subX = subsampling_x if plane > 0 else 0
        subY = subsampling_y if plane > 0 else 0
        sbWidth4 = (tile_group.MiColEnd - c) >> subX
        sbHeight4 = (tile_group.MiRowEnd - r) >> subY

        for y in range(-1, (sbSize4 >> subY) + 1):
            for x in range(-1, (sbSize4 >> subX) + 1):
                if y < 0 and x < sbWidth4:
                    tile_group.BlockDecoded[plane][y][x] = 1  # 上方参考像素已解码
                elif x < 0 and y < sbHeight4:
                    tile_group.BlockDecoded[plane][y][x] = 1  # 左侧参考像素已解码
                else:
                    tile_group.BlockDecoded[plane][y][x] = 0  # 当前块未解码

        tile_group.BlockDecoded[plane][sbSize4 >> subY][-1] = 0

清除块解码标志,标记哪些块已经解码(用于参考),哪些块尚未解码。

  • 上方参考像素:标记为已解码(BlockDecoded = 1
  • 左侧参考像素:标记为已解码(BlockDecoded = 1
  • 当前Superblock内的块:标记为未解码(BlockDecoded = 0

4. 读取Loop Restoration参数

位置: src/tile/tile_group.py - __read_lr()

规范文档: 5.11.57 Read loop restoration syntax

def __read_lr(self, av1: AV1Decoder, r: int, c: int, sbSize: int):
    """
    读取Loop Restoration参数
    规范文档 5.11.57 Read loop restoration syntax
    """
    seq_header = av1.seq_header
    frame_header = av1.frame_header
    subsampling_x = seq_header.color_config.subsampling_x
    subsampling_y = seq_header.color_config.subsampling_y
    NumPlanes = seq_header.color_config.NumPlanes

    if frame_header.allow_intrabc:
        return

    w = Num_4x4_Blocks_Wide[sbSize]
    h = Num_4x4_Blocks_High[sbSize]

    for plane in range(NumPlanes):
        if frame_header.FrameRestorationType[plane] != FRAME_RESTORATION_TYPE.RESTORE_NONE:
            # 计算Loop Restoration单元的范围
            # ... 计算unitRowStart, unitRowEnd, unitColStart, unitColEnd ...
            for unitRow in range(unitRowStart, unitRowEnd):
                for unitCol in range(unitColStart, unitColEnd):
                    self.__read_lr_unit(av1, plane, unitRow, unitCol)

读取Loop Restoration(环路恢复)参数,用于后续的后处理阶段。

  • 如果启用了Loop Restoration,需要读取每个恢复单元的参数
  • 参数包括Wiener滤波系数或SGR投影参数

5. 解码块划分

位置: src/tile/tile_group.py - __decode_partition()

规范文档: 5.11.4 Decode partition syntax

这是Superblock解码的核心步骤,递归解码块的划分方式。

def __decode_partition(self, av1: AV1Decoder, r: int, c: int, bSize: SUB_SIZE):
    """
    解码划分
    规范文档 5.11.4 Decode partition syntax
    """
    frame_header = av1.frame_header
    tile_group = self.tile_group
    MiRows = frame_header.MiRows
    MiCols = frame_header.MiCols

    if r >= MiRows or c >= MiCols:
        return 0

    # 检查上方和左侧块是否可用
    tile_group.AvailU = is_inside(av1, r - 1, c)
    tile_group.AvailL = is_inside(av1, r, c - 1)
    
    # 计算块尺寸和边界检查
    num4x4 = Num_4x4_Blocks_Wide[bSize]
    halfBlock4x4 = num4x4 >> 1
    quarterBlock4x4 = halfBlock4x4 >> 1
    hasRows = (r + halfBlock4x4) < MiRows
    hasCols = (c + halfBlock4x4) < MiCols

    # 解码划分模式
    if bSize < SUB_SIZE.BLOCK_8X8:
        tile_group.partition = PARTITION.PARTITION_NONE
    elif hasRows and hasCols:
        tile_group.partition = read_S(av1, "partition", r=r, c=c, bSize=bSize)
    elif hasCols:
        split_or_horz = read_S(av1, "split_or_horz", r=r, c=c, bSize=bSize)
        tile_group.partition = PARTITION.PARTITION_SPLIT if split_or_horz else PARTITION.PARTITION_HORZ
    elif hasRows:
        split_or_vert = read_S(av1, "split_or_vert", r=r, c=c, bSize=bSize)
        tile_group.partition = PARTITION.PARTITION_SPLIT if split_or_vert else PARTITION.PARTITION_VERT
    else:
        tile_group.partition = PARTITION.PARTITION_SPLIT

    # 根据划分模式递归解码子块
    subSize: SUB_SIZE = Partition_Subsize[tile_group.partition][bSize]
    
    if tile_group.partition == PARTITION.PARTITION_NONE:
        self.__decode_block(av1, r, c, subSize)
    elif tile_group.partition == PARTITION.PARTITION_SPLIT:
        # 递归解码4个子块
        self.__decode_partition(av1, r, c, subSize)
        self.__decode_partition(av1, r, c + halfBlock4x4, subSize)
        self.__decode_partition(av1, r + halfBlock4x4, c, subSize)
        self.__decode_partition(av1, r + halfBlock4x4, c + halfBlock4x4, subSize)
    # ... 其他划分模式 ...

块划分解码是递归过程,根据划分模式将Superblock划分为更小的块,直到达到最小块尺寸或PARTITION_NONE


Superblock遍历顺序

Superblock按照光栅扫描顺序遍历:

for r in range(tile_group.MiRowStart, tile_group.MiRowEnd, sbSize4):
    clear_left_context(av1)  # 每行开始时清除左侧上下文
    for c in range(tile_group.MiColStart, tile_group.MiColEnd, sbSize4):
        # 处理Superblock (r, c)
  • 行优先:从上到下逐行处理
  • 列优先:每行内从左到右处理
  • 上下文管理:每行开始时清除左侧上下文,确保行间独立性

关键数据结构

Superblock相关参数

# Superblock尺寸
sbSize: SUB_SIZE          # Superblock尺寸(BLOCK_64X64或BLOCK_128X128)
sbSize4: int              # Superblock尺寸(4x4块单位)

# Superblock位置
r: int                    # Superblock行位置(MI单位)
c: int                    # Superblock列位置(MI单位)

# CDEF索引
cdef_idx: List[List[int]]  # CDEF索引数组,用于存储CDEF参数索引

块解码标志

BlockDecoded: List[List[List[int]]]  # BlockDecoded[plane][row][col]
# -1 <= row <= 32, -1 <= col <= 32
# 0: 未解码
# 1: 已解码(可用作参考)

与Tile解码的集成

Superblock解码是Tile解码的核心部分:

Tile解码(__decode_tile)
  → 初始化上下文和参数
  → 遍历Superblock
    → 清除左侧上下文(每行)
    → 对每个Superblock:
      → 清除CDEF上下文
      → 清除块解码标志
      → 读取Loop Restoration参数
      → 解码块划分(递归)
        → 解码块模式信息
        → 解码预测
        → 解码残差
        → 重建块

总结

Superblock解码是AV1解码的基础,主要工作包括:

  1. 上下文管理:清除和初始化各种上下文信息
  2. 参数读取:读取CDEF和Loop Restoration参数
  3. 块划分:递归解码块的划分方式
  4. 独立性保证:确保每个Superblock可以独立解码

Superblock解码为后续的块级解码(模式信息、预测、残差)奠定了基础,是AV1高效解码的关键环节。


参考资源:

上一篇: Tile划分和并行处理
下一篇: 块划分(Block Partitioning)

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