Coverage Report

Created: 2025-07-01 06:50

/rust/registry/src/index.crates.io-6f17d22bba15001f/rav1e-0.7.1/src/segmentation.rs
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2018-2023, The rav1e contributors. All rights reserved
2
//
3
// This source code is subject to the terms of the BSD 2 Clause License and
4
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
5
// was not distributed with this source code in the LICENSE file, you can
6
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
7
// Media Patent License 1.0 was not distributed with this source code in the
8
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
9
10
use crate::context::*;
11
use crate::header::PRIMARY_REF_NONE;
12
use crate::partition::BlockSize;
13
use crate::rdo::spatiotemporal_scale;
14
use crate::rdo::DistortionScale;
15
use crate::tiling::TileStateMut;
16
use crate::util::Pixel;
17
use crate::FrameInvariants;
18
use crate::FrameState;
19
20
pub const MAX_SEGMENTS: usize = 8;
21
22
0
#[profiling::function]
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize::<u16>
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize::<u8>
23
pub fn segmentation_optimize<T: Pixel>(
24
  fi: &FrameInvariants<T>, fs: &mut FrameState<T>,
25
) {
26
  assert!(fi.enable_segmentation);
27
  fs.segmentation.enabled = true;
28
29
  if fs.segmentation.enabled {
30
    fs.segmentation.update_map = true;
31
32
    // We don't change the values between frames.
33
    fs.segmentation.update_data = fi.primary_ref_frame == PRIMARY_REF_NONE;
34
35
    // Avoid going into lossless mode by never bringing qidx below 1.
36
    // Because base_q_idx changes more frequently than the segmentation
37
    // data, it is still possible for a segment to enter lossless, so
38
    // enforcement elsewhere is needed.
39
    let offset_lower_limit = 1 - fi.base_q_idx as i16;
40
41
    if !fs.segmentation.update_data {
42
      let mut min_segment = MAX_SEGMENTS;
43
      for i in 0..MAX_SEGMENTS {
44
        if fs.segmentation.features[i][SegLvl::SEG_LVL_ALT_Q as usize]
45
          && fs.segmentation.data[i][SegLvl::SEG_LVL_ALT_Q as usize]
46
            >= offset_lower_limit
47
        {
48
          min_segment = i;
49
          break;
50
        }
51
      }
52
      assert_ne!(min_segment, MAX_SEGMENTS);
53
      fs.segmentation.min_segment = min_segment as u8;
54
      fs.segmentation.update_threshold(fi.base_q_idx, fi.config.bit_depth);
55
      return;
56
    }
57
58
    segmentation_optimize_inner(fi, fs, offset_lower_limit);
59
60
    /* Figure out parameters */
61
    fs.segmentation.preskip = false;
62
    fs.segmentation.last_active_segid = 0;
63
    for i in 0..MAX_SEGMENTS {
64
      for j in 0..SegLvl::SEG_LVL_MAX as usize {
65
        if fs.segmentation.features[i][j] {
66
          fs.segmentation.last_active_segid = i as u8;
67
          if j >= SegLvl::SEG_LVL_REF_FRAME as usize {
68
            fs.segmentation.preskip = true;
69
          }
70
        }
71
      }
72
    }
73
  }
74
}
75
76
// Select target quantizers for each segment by fitting to log(scale).
77
0
fn segmentation_optimize_inner<T: Pixel>(
78
0
  fi: &FrameInvariants<T>, fs: &mut FrameState<T>, offset_lower_limit: i16,
79
0
) {
80
  use crate::quantize::{ac_q, select_ac_qi};
81
  use crate::util::kmeans;
82
  use arrayvec::ArrayVec;
83
84
  // Minimize the total distance from a small set of values to all scales.
85
  // Find k-means of log(spatiotemporal scale), k in 3..=8
86
0
  let c: ([_; 8], [_; 7], [_; 6], [_; 5], [_; 4], [_; 3]) = {
87
0
    let spatiotemporal_scores =
88
0
      &fi.coded_frame_data.as_ref().unwrap().spatiotemporal_scores;
89
0
    let mut log2_scale_q11 = Vec::with_capacity(spatiotemporal_scores.len());
90
0
    log2_scale_q11.extend(spatiotemporal_scores.iter().map(|&s| s.blog16()));
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u16>::{closure#0}
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u8>::{closure#0}
91
0
    log2_scale_q11.sort_unstable();
92
0
    let l = &log2_scale_q11;
93
0
    (kmeans(l), kmeans(l), kmeans(l), kmeans(l), kmeans(l), kmeans(l))
94
0
  };
95
0
96
0
  // Find variance in spacing between successive log(scale)
97
0
  let var = |c: &[i16]| {
98
0
    let delta = ArrayVec::<_, MAX_SEGMENTS>::from_iter(
99
0
      c.iter().skip(1).zip(c).map(|(&a, &b)| b as i64 - a as i64),
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u16>::{closure#1}::{closure#0}
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u8>::{closure#1}::{closure#0}
100
0
    );
101
0
    let mean = delta.iter().sum::<i64>() / delta.len() as i64;
102
0
    delta.iter().map(|&d| (d - mean).pow(2)).sum::<i64>() as u64
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u16>::{closure#1}::{closure#1}
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u8>::{closure#1}::{closure#1}
103
0
  };
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u16>::{closure#1}
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u8>::{closure#1}
104
0
  let variance =
105
0
    [var(&c.0), var(&c.1), var(&c.2), var(&c.3), var(&c.4), var(&c.5)];
106
0
107
0
  // Choose the k value with minimal variance in spacing
108
0
  let min_variance = *variance.iter().min().unwrap();
109
0
  let position = variance.iter().rposition(|&v| v == min_variance).unwrap();
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u16>::{closure#2}
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u8>::{closure#2}
110
0
111
0
  // For the selected centroids, derive a target quantizer:
112
0
  //   scale Q'^2 = Q^2
113
0
  // See `distortion_scale_for` for more information.
114
0
  let compute_delta = |centroids: &[i16]| {
115
    use crate::util::{bexp64, blog64};
116
0
    let log2_base_ac_q_q57 =
117
0
      blog64(ac_q(fi.base_q_idx, 0, fi.config.bit_depth).get().into());
118
0
    centroids
119
0
      .iter()
120
0
      .rev()
121
0
      // Rewrite in log form and exponentiate:
122
0
      //   scale Q'^2 = Q^2
123
0
      //           Q' = Q / sqrt(scale)
124
0
      //      log(Q') = log(Q) - 0.5 log(scale)
125
0
      .map(|&log2_scale_q11| {
126
0
        bexp64(log2_base_ac_q_q57 - ((log2_scale_q11 as i64) << (57 - 11 - 1)))
127
0
      })
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u16>::{closure#3}::{closure#0}
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u8>::{closure#3}::{closure#0}
128
0
      // Find the index of the nearest quantizer to the target,
129
0
      // and take the delta from the base quantizer index.
130
0
      .map(|q| {
131
0
        // Avoid going into lossless mode by never bringing qidx below 1.
132
0
        select_ac_qi(q, fi.config.bit_depth).max(1) as i16
133
0
          - fi.base_q_idx as i16
134
0
      })
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u16>::{closure#3}::{closure#1}
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u8>::{closure#3}::{closure#1}
135
0
      .collect::<ArrayVec<_, MAX_SEGMENTS>>()
136
0
  };
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u16>::{closure#3}
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u8>::{closure#3}
137
138
  // Compute segment deltas for best value of k
139
0
  let seg_delta = match position {
140
0
    0 => compute_delta(&c.0),
141
0
    1 => compute_delta(&c.1),
142
0
    2 => compute_delta(&c.2),
143
0
    3 => compute_delta(&c.3),
144
0
    4 => compute_delta(&c.4),
145
0
    _ => compute_delta(&c.5),
146
  };
147
148
  // Update the segmentation data
149
0
  fs.segmentation.min_segment = 0;
150
0
  fs.segmentation.max_segment = seg_delta.len() as u8 - 1;
151
0
  for (&delta, (features, data)) in seg_delta
152
0
    .iter()
153
0
    .zip(fs.segmentation.features.iter_mut().zip(&mut fs.segmentation.data))
154
0
  {
155
0
    features[SegLvl::SEG_LVL_ALT_Q as usize] = true;
156
0
    data[SegLvl::SEG_LVL_ALT_Q as usize] = delta.max(offset_lower_limit);
157
0
  }
158
159
0
  fs.segmentation.update_threshold(fi.base_q_idx, fi.config.bit_depth);
160
0
}
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u16>
Unexecuted instantiation: rav1e::segmentation::segmentation_optimize_inner::<u8>
161
162
0
#[profiling::function]
Unexecuted instantiation: rav1e::segmentation::select_segment::<u16>
Unexecuted instantiation: rav1e::segmentation::select_segment::<u8>
163
pub fn select_segment<T: Pixel>(
164
  fi: &FrameInvariants<T>, ts: &TileStateMut<'_, T>, tile_bo: TileBlockOffset,
165
  bsize: BlockSize, skip: bool,
166
) -> std::ops::RangeInclusive<u8> {
167
  // If skip is true or segmentation is turned off, sidx is not coded.
168
  if skip || !fi.enable_segmentation {
169
    return 0..=0;
170
  }
171
172
  use crate::api::SegmentationLevel;
173
  if fi.config.speed_settings.segmentation == SegmentationLevel::Full {
174
    return ts.segmentation.min_segment..=ts.segmentation.max_segment;
175
  }
176
177
  let frame_bo = ts.to_frame_block_offset(tile_bo);
178
  let scale = spatiotemporal_scale(fi, frame_bo, bsize);
179
180
  let sidx = segment_idx_from_distortion(&ts.segmentation.threshold, scale);
181
182
  // Avoid going into lossless mode by never bringing qidx below 1.
183
  let sidx = sidx.max(ts.segmentation.min_segment);
184
185
  if fi.config.speed_settings.segmentation == SegmentationLevel::Complex {
186
    return sidx..=ts.segmentation.max_segment.min(sidx.saturating_add(1));
187
  }
188
189
  sidx..=sidx
190
}
191
192
0
fn segment_idx_from_distortion(
193
0
  threshold: &[DistortionScale; MAX_SEGMENTS - 1], s: DistortionScale,
194
0
) -> u8 {
195
0
  threshold.partition_point(|&t| s.0 < t.0) as u8
196
0
}