角度(Angular)模式通过 33 种精细的方向来模拟图像的纹理和边缘。

其核心流程可以概括为:为块内每个像素点,沿着指定角度反向投影到参考像素行/列上,再通过插值计算出预测值

第 1 步:模式与角度的映射

首先,33 个角度模式(模式号 2 到 34)需要映射为具体的几何角度和位移参数。这些模式被分为两大类:

  • 水平类模式 (Horizontal-like, 模式 2-17):预测角度比 45 度更接近水平。主要使用左侧的参考像素列进行预测。
  • 垂直类模式 (Vertical-like, 模式 18-34):预测角度比 45 度更接近垂直。主要使用上方的参考像素行进行预测。

每个模式都对应一个整数化的角度位移参数 intraPredAngle。这个参数的绝对值越大,表示角度越偏离纯粹的水平或垂直。这是一个预定义的查找表。

模式 (Mode) intraPredAngle 角度 (近似)
10 (水平) 32
11 26 -7°
17 2 -45°
18 -2 45°
26 (垂直) 0 90°
34 -32 180°

注意:intraPredAngle 是一个位移因子,32 对应 tan(angle)32 倍。例如,intraPredAngle = 32 意味着每向下移动一个像素,就向右平移 32/32 = 1 个像素。

第 2 步:构建扩展参考像素数组

为了处理各种角度的投影,算法需要一个比块尺寸更大的参考像素集合。它会根据模式的类别(水平类/垂直类)来构建一个一维的扩展参考数组 ref[]

对于垂直类模式 (18-34):

  • 主参考 (Main Array)ref[] 的主体是上方的参考像素 p[x][-1],从左上角 p[-1][-1] 一直延伸到右上角 p[2N-1][-1]
  • 侧参考 (Side Array):当投影角度非常陡峭,导致投影点为负数时,需要用到左侧的参考像素。

ASCII 图示 (垂直类模式):

     <- Main Array ->
p[-1][-1] p[0][-1] ... p[N-1][-1] ... p[2N-1][-1]
   +----------------+
   |  (x,y)         |
   |     * |
   |   /            |
   |  / (投影)      |
   | /              |
   +----------------+

对于水平类模式 (2-17):

  • 主参考 (Main Array)ref[] 的主体是左侧的参考像素 p[-1][y],从左上角 p[-1][-1] 一直延伸到左下角 p[-1][2N-1]
  • 侧参考 (Side Array):当投影角度非常平缓时,需要用到上方的参考像素。

第 3 步:逐像素投影与插值计算

这是算法的核心,对块内的每一个像素 (x, y)x, y 从 0 到 N-1)循环执行。我们以垂直类模式 (mode ≥ 18) 为例:

  1. 计算整数投影位置 i 和小数位移 f
    • 首先计算一个总位移:delta_pos = (y + 1) * intraPredAngle
    • 这个位移 delta_pos 是一个带小数的偏移量,在实现中用 32 倍的整数表示。
    • 小数位移 f (fraction) 是总位移的低 5 位:f = delta_pos & 31
    • 整数投影位置的偏移是总位移的高位(带符号右移 5 位,相当于除以 32):delta_ref = delta_pos >> 5
  2. 确定最终参考像素索引 ref_idx
    • 最终的索引是当前像素的 x 坐标加上这个整数偏移:ref_idx = x + delta_ref
  3. 插值计算预测值
    • 情况 A:无小数位移 (f == 0)
      • 投影点正好落在整数位置上,无需插值。
      • 直接复制参考像素:pred(x, y) = ref[ref_idx]
    • 情况 B:有小数位移 (f != 0)
      • 投影点落在两个参考像素 ref[ref_idx]ref[ref_idx + 1] 之间。
      • 使用线性插值计算预测值: pred(x, y) = ((32 - f) * ref[ref_idx] + f * ref[ref_idx + 1] + 16) >> 5
      • 这个公式是一个加权平均:pred ≈ ref[ref_idx] * (1 - f/32) + ref[ref_idx+1] * (f/32)+16 是为了四舍五入,>>5 是除以 32。

水平类模式 (mode < 18) 的计算过程完全类似,只是 xy 的角色互换。

第 4 步:特殊情况处理 (负索引投影)

ref_idx 计算出来是负数时,意味着投影点落在了左上角 p[-1][-1] 的左边或上边。这时就需要用到侧参考数组,并且插值计算方式会略有不同,通常是使用一个预定义的 invAngle 参数(角度的倒数)来进行二次投影。

例如,对于垂直模式,当 ref_idx 为 -1 时,说明投影到了 p[-1][-1]p[-2][-1] 之间(p[-2][-1] 并不存在),此时会改用 invAnglep[-1][-1] 沿着左侧参考列向下投影来计算。

总结

对于块内每一个像素 (x, y):

  1. 输入:Intra 模式号、xy 坐标、扩展参考像素数组 ref[]
  2. 查找:根据模式号查到 intraPredAngle
  3. 计算投影
    • delta_pos = (y+1) * intraPredAngle (以垂直模式为例)
    • f = delta_pos & 31
    • ref_idx = x + (delta_pos >> 5)
  4. 插值
    • 如果 f == 0pred(x,y) = ref[ref_idx]
    • 如果 f != 0pred(x,y) = ((32-f)*ref[ref_idx] + f*ref[ref_idx+1] + 16) >> 5
  5. 输出:预测像素值 pred(x,y)