/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 | } |