3.1 Tile划分和并行处理
作者:chai51
出处:https://www.cnblogs.com/chai51
版权:本文版权归作者和博客园共有
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
引言
Tile(瓦片)是AV1并行解码的基本单元,它将一帧图像划分为多个独立的矩形区域,每个Tile可以独立解码,从而实现并行处理。Tile划分和并行处理是AV1解码器实现高效解码的关键技术之一。
源码说明: 本文档基于作者自己编写的AV1解码器Python实现,所有代码示例和实现细节均来自实际可运行的源码。源码仓库:GitHub - av1_learning
Tile概述
Tile的基本概念
Tile是AV1帧内可独立解码的矩形区域,具有以下特点:
- 独立性:每个Tile可以独立解码,不依赖其他Tile的数据
- 并行性:多个Tile可以并行解码,提高解码效率
- 边界限制:Tile边界必须对齐到Superblock边界
- 尺寸限制:每个Tile的最大宽度和面积有规范限制
Tile与Superblock的关系
- Superblock:AV1的基本编码单元,可以是64x64或128x128像素
- Tile:由多个Superblock组成的矩形区域
- Tile边界必须对齐到Superblock边界,确保解码的独立性
Tile信息解析
位置: src/frame/frame_header.py - __tile_info()
规范文档: 5.9.15 Tile info syntax
解析流程
-
计算Superblock数量
sbCols = ((MiCols + 31) >> 5) if use_128x128_superblock else ((MiCols + 15) >> 4) sbRows = ((MiRows + 31) >> 5) if use_128x128_superblock else ((MiRows + 15) >> 4)- 根据Superblock尺寸(64x64或128x128)计算帧内的Superblock列数和行数
-
计算Tile划分参数
maxTileWidthSb = MAX_TILE_WIDTH >> sbSize maxTileAreaSb = MAX_TILE_AREA >> (2 * sbSize) minLog2TileCols = self.__tile_log2(maxTileWidthSb, sbCols) maxLog2TileCols = self.__tile_log2(1, min(sbCols, MAX_TILE_COLS)) maxLog2TileRows = self.__tile_log2(1, min(sbRows, MAX_TILE_ROWS))- 计算Tile的最大宽度和面积限制
- 计算Tile列数和行数的对数范围
-
Tile间距模式
uniform_tile_spacing_flag = read_f(reader, 1) if uniform_tile_spacing_flag: # 均匀Tile间距 frame_header.TileColsLog2 = minLog2TileCols frame_header.TileRowsLog2 = minLog2TileRows # 计算Tile列数和行数 frame_header.TileCols = 1 << frame_header.TileColsLog2 frame_header.TileRows = 1 << frame_header.TileRowsLog2 else: # 非均匀Tile间距 # 解码每个Tile的边界位置- 均匀间距:所有Tile尺寸相同,只需解码列数和行数的对数
- 非均匀间距:每个Tile的边界位置需要单独解码
-
计算Tile边界
# 计算每个Tile的起始和结束位置 for i in range(frame_header.TileCols + 1): frame_header.MiColStarts[i] = ... for i in range(frame_header.TileRows + 1): frame_header.MiRowStarts[i] = ...- 计算每个Tile在MI(最小单元)坐标系中的起始和结束位置
- 存储到
MiColStarts和MiRowStarts数组中
Tile Group OBU结构
位置: src/tile/tile_group.py - tile_group_obu()
规范文档: 5.11.1 General tile group OBU syntax
OBU结构
Tile Group OBU包含一个或多个Tile的数据,结构如下:
def tile_group_obu(self, av1: AV1Decoder, sz: int) -> TileGroup:
# 1. 解析Tile Group头部
NumTiles = frame_header.TileCols * frame_header.TileRows
if NumTiles > 1:
tile_start_and_end_present_flag = read_f(reader, 1)
# 2. 确定Tile范围
if NumTiles == 1 or not tile_start_and_end_present_flag:
tg_start = 0
tg_end = NumTiles - 1
else:
tileBits = frame_header.TileColsLog2 + frame_header.TileRowsLog2
tg_start = read_f(reader, tileBits)
tg_end = read_f(reader, tileBits)
# 3. 解码每个Tile
frame_header.TileNum = tg_start
while frame_header.TileNum <= tg_end:
# 计算Tile位置
tileRow = frame_header.TileNum // frame_header.TileCols
tileCol = frame_header.TileNum % frame_header.TileCols
# 解析Tile大小(最后一个Tile除外)
if not lastTile:
tile_size_minus_1 = read_le(reader, frame_header.TileSizeBytes)
tileSize = tile_size_minus_1 + 1
# 设置Tile边界
tile_group.MiRowStart = frame_header.MiRowStarts[tileRow]
tile_group.MiRowEnd = frame_header.MiRowStarts[tileRow + 1]
tile_group.MiColStart = frame_header.MiColStarts[tileCol]
tile_group.MiColEnd = frame_header.MiColStarts[tileCol + 1]
# 解码Tile
decoder.init_symbol(av1, tileSize)
self.__decode_tile(av1)
decoder.exit_symbol(av1)
frame_header.TileNum += 1
Tile Group头部字段
- tile_start_and_end_present_flag:是否包含Tile起始和结束索引
- tg_start:Tile Group中第一个Tile的索引
- tg_end:Tile Group中最后一个Tile的索引
- tile_size_minus_1:每个Tile的大小(最后一个Tile除外)
Tile解码过程
位置: src/tile/tile_group.py - __decode_tile()
规范文档: 5.11.2 Decode tile syntax
解码流程
def __decode_tile(self, av1: AV1Decoder):
# 1. 初始化上下文
clear_above_context(av1)
tile_group.DeltaLF = [0] * FRAME_LF_COUNT
# 2. 初始化参考参数
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]
# 3. 遍历Superblock
sbSize = SUB_SIZE.BLOCK_128X128 if use_128x128_superblock else SUB_SIZE.BLOCK_64X64
sbSize4 = Num_4x4_Blocks_Wide[sbSize]
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
self.__clear_cdef(av1, r, c)
self.__clear_block_decoded_flags(av1, r, c, sbSize4)
self.__read_lr(av1, r, c, sbSize)
# 解码块划分
self.__decode_partition(av1, r, c, sbSize)
关键步骤
-
上下文初始化
- 清除上方上下文(
clear_above_context) - 初始化Delta LF参数
- 初始化Loop Restoration参考参数
- 清除上方上下文(
-
Superblock遍历
- 按光栅扫描顺序遍历Tile内的所有Superblock
- 每行开始时清除左侧上下文(
clear_left_context)
-
块解码标志初始化
- 清除CDEF索引
- 清除块解码标志
- 读取Loop Restoration参数
并行处理机制
Tile的独立性
Tile的独立性体现在以下几个方面:
- 数据独立性:每个Tile包含完整的解码所需数据
- 上下文独立性:Tile边界处的上下文被重置
- 熵解码独立性:每个Tile有独立的熵解码上下文
并行解码策略
虽然AV1规范支持Tile的并行解码,但实际实现中需要考虑:
- 依赖关系:某些帧级操作(如CDF更新)需要在所有Tile解码完成后进行
- 内存管理:并行解码需要合理管理内存访问
- 同步机制:帧级操作需要等待所有Tile完成
上下文管理
Tile边界处的上下文管理:
- 上方上下文:每行Superblock开始时清除
- 左侧上下文:每个Superblock开始时清除
- CDEF上下文:每个Superblock开始时清除
- Loop Restoration上下文:每个Superblock开始时读取
Tile划分流程图
Tile信息解析流程
Tile信息解析是在帧头中完成的,主要目的是确定如何将一帧图像划分成多个Tile。这个过程分为两种情况:
情况1:均匀划分 - 所有Tile大小相同,只需要计算边界位置
情况2:非均匀划分 - Tile大小可以不同,需要从比特流中读取每个Tile的边界位置
Tile Group OBU解析流程
Tile Group OBU包含了实际的图像数据,解析流程如下:
- 确定要解码哪些Tile:一个Tile Group OBU可能包含多个Tile,需要确定起始和结束位置
- 逐个解码每个Tile:对每个Tile,先读取大小信息,然后解码Tile内的数据
- 每个Tile独立解码:每个Tile有独立的熵解码上下文,互不干扰
流程说明:
- Tile范围:一个Tile Group OBU可以只包含部分Tile,这样可以实现更灵活的数据组织
- Tile大小:除了最后一个Tile,其他Tile的大小都会明确记录在比特流中
- 独立解码:每个Tile解码前都会初始化熵解码器,解码后退出,确保Tile之间的独立性
关键数据结构
FrameHeader中的Tile信息
class FrameHeader:
# Tile划分参数
TileColsLog2: int # Tile列数的对数
TileRowsLog2: int # Tile行数的对数
TileCols: int # Tile列数
TileRows: int # Tile行数
# Tile边界
MiColStarts: List[int] # 每个Tile列的起始MI位置
MiRowStarts: List[int] # 每个Tile行的起始MI位置
# Tile大小
TileSizeBytes: int # Tile大小字段的字节数
TileNum: int # 当前Tile编号
TileGroup中的Tile信息
class TileGroup:
# Tile边界
MiRowStart: int # 当前Tile的起始行
MiRowEnd: int # 当前Tile的结束行
MiColStart: int # 当前Tile的起始列
MiColEnd: int # 当前Tile的结束列
# 上下文管理
BlockDecoded: List[List[List[int]]] # 块解码标志
DeltaLF: List[int] # Delta LF参数
RefSgrXqd: List[List[List[int]]] # Loop Restoration参考参数
RefLrWiener: List[List[List[List[int]]]] # Wiener滤波参考参数
总结
Tile划分和并行处理是AV1解码器实现高效解码的关键技术。通过将一帧图像划分为多个独立的Tile,AV1可以实现:
- 并行解码:多个Tile可以同时解码,提高解码效率
- 内存管理:每个Tile有独立的上下文,便于内存管理
- 错误隔离:一个Tile的错误不会影响其他Tile
Tile划分在帧头中完成,Tile Group OBU包含Tile的实际数据。每个Tile可以独立解码,但帧级操作(如CDF更新)需要在所有Tile完成后进行。
参考资源:
- AV1规范文档
- 源码实现: GitHub - av1_learning
- Tile信息解析:
src/frame/frame_header.py - Tile Group解析:
src/tile/tile_group.py
- Tile信息解析:
下一篇: Superblock解码

浙公网安备 33010602011771号