Skip to main content

oximedia_optimize/transform/
quant.rs

1//! Adaptive quantization optimization.
2
3/// Adaptive QP calculator.
4#[derive(Debug, Clone, Copy)]
5pub struct AdaptiveQp {
6    /// Base QP.
7    pub base_qp: u8,
8    /// QP offset.
9    pub offset: i8,
10    /// Effective QP.
11    pub effective_qp: u8,
12}
13
14impl AdaptiveQp {
15    /// Creates a new adaptive QP.
16    #[must_use]
17    pub fn new(base_qp: u8, offset: i8) -> Self {
18        let effective_qp = (i16::from(base_qp) + i16::from(offset)).clamp(0, 63) as u8;
19
20        Self {
21            base_qp,
22            offset,
23            effective_qp,
24        }
25    }
26}
27
28/// Quantization optimizer.
29pub struct QuantizationOptimizer {
30    enable_trellis: bool,
31    deadzone_offset: f64,
32}
33
34impl Default for QuantizationOptimizer {
35    fn default() -> Self {
36        Self::new(false, 0.0)
37    }
38}
39
40impl QuantizationOptimizer {
41    /// Creates a new quantization optimizer.
42    #[must_use]
43    pub fn new(enable_trellis: bool, deadzone_offset: f64) -> Self {
44        Self {
45            enable_trellis,
46            deadzone_offset,
47        }
48    }
49
50    /// Quantizes coefficients with optional trellis optimization.
51    #[allow(dead_code)]
52    #[must_use]
53    pub fn quantize(&self, coeffs: &[i16], qp: u8) -> Vec<i16> {
54        if self.enable_trellis {
55            self.trellis_quantize(coeffs, qp)
56        } else {
57            self.simple_quantize(coeffs, qp)
58        }
59    }
60
61    fn simple_quantize(&self, coeffs: &[i16], qp: u8) -> Vec<i16> {
62        let scale = self.qp_to_scale(qp);
63        let deadzone = (f64::from(scale) * self.deadzone_offset) as i16;
64
65        coeffs
66            .iter()
67            .map(|&c| if c.abs() < deadzone { 0 } else { c / scale })
68            .collect()
69    }
70
71    fn trellis_quantize(&self, coeffs: &[i16], qp: u8) -> Vec<i16> {
72        // Simplified trellis quantization
73        // Real implementation would use dynamic programming
74        let scale = self.qp_to_scale(qp);
75        let mut quantized = vec![0i16; coeffs.len()];
76
77        for (i, &c) in coeffs.iter().enumerate() {
78            let q = c / scale;
79
80            // Try q, q-1, q+1 and pick best
81            let candidates = [q.saturating_sub(1), q, q.saturating_add(1)];
82            let mut best_q = q;
83            let mut best_cost = f64::MAX;
84
85            for &candidate in &candidates {
86                let reconstructed = candidate * scale;
87                let distortion = (c - reconstructed).abs();
88                let rate = if candidate == 0 { 0 } else { candidate.abs() };
89                let cost = f64::from(distortion) + f64::from(rate);
90
91                if cost < best_cost {
92                    best_cost = cost;
93                    best_q = candidate;
94                }
95            }
96
97            quantized[i] = best_q;
98        }
99
100        quantized
101    }
102
103    fn qp_to_scale(&self, qp: u8) -> i16 {
104        // Simplified QP to scale conversion
105        // Real implementation would use proper QP tables
106        (1 << (qp / 6)).max(1)
107    }
108
109    /// Calculates optimal deadzone for a block.
110    #[allow(dead_code)]
111    #[must_use]
112    pub fn calculate_deadzone(&self, variance: f64) -> f64 {
113        // Larger deadzone for low variance (flat areas)
114        if variance < 100.0 {
115            1.5
116        } else if variance < 500.0 {
117            1.0
118        } else {
119            0.5
120        }
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_adaptive_qp() {
130        let qp = AdaptiveQp::new(26, 3);
131        assert_eq!(qp.base_qp, 26);
132        assert_eq!(qp.offset, 3);
133        assert_eq!(qp.effective_qp, 29);
134    }
135
136    #[test]
137    fn test_adaptive_qp_clamping() {
138        let qp_high = AdaptiveQp::new(60, 10);
139        assert_eq!(qp_high.effective_qp, 63); // Clamped
140
141        let qp_low = AdaptiveQp::new(5, -10);
142        assert_eq!(qp_low.effective_qp, 0); // Clamped
143    }
144
145    #[test]
146    fn test_quantization_optimizer_creation() {
147        let optimizer = QuantizationOptimizer::default();
148        assert!(!optimizer.enable_trellis);
149        assert_eq!(optimizer.deadzone_offset, 0.0);
150    }
151
152    #[test]
153    fn test_simple_quantize() {
154        let optimizer = QuantizationOptimizer::default();
155        let coeffs = vec![100, 50, 25, 10, 5];
156        let quantized = optimizer.simple_quantize(&coeffs, 12);
157        assert!(quantized.iter().all(|&q| q <= 100));
158    }
159
160    #[test]
161    fn test_deadzone_calculation() {
162        let optimizer = QuantizationOptimizer::default();
163        let dz_low = optimizer.calculate_deadzone(50.0);
164        let dz_high = optimizer.calculate_deadzone(1000.0);
165        assert!(dz_low > dz_high); // Larger deadzone for low variance
166    }
167}