1#![allow(non_upper_case_globals)]
2
3use std::cmp::{max, min};
4use std::fmt;
5use std::os::raw::c_uint;
6type Coef = c_uint;
7
8pub struct QTable {
9 pub(crate) coeffs: [Coef; 64],
10}
11
12impl PartialEq for QTable {
13 fn eq(&self, other: &Self) -> bool {
14 let iter2 = other.coeffs.iter().copied();
15 self.coeffs.iter().copied().zip(iter2).all(|(s, o)| s == o)
16 }
17}
18
19impl fmt::Debug for QTable {
20 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
21 write!(fmt, "QTable{{coeffs:{:?}}}", &self.coeffs[..])
22 }
23}
24
25const low_weights : [f32; 19] = [
26 1.00, 0.85, 0.55, 0., 0., 0., 0., 0.,
27 0.85, 0.75, 0.10, 0., 0., 0., 0., 0.,
28 0.55, 0.10, 0.05,
29];
30
31impl QTable {
32 #[must_use]
33 pub fn compare(&self, other: &Self) -> (f32, f32) {
34 let mut scales = [0.; 64];
35 for (s, (&a, &b)) in scales.iter_mut().zip(self.coeffs.iter().zip(other.coeffs.iter())) {
36 *s = if b > 0 { a as f32 / b as f32 } else { 0. };
37 }
38 let avg = scales.iter().sum::<f32>() / 64.;
39 let var = scales.iter().map(|&v| (v - avg).powi(2)).sum::<f32>() / 64.;
40 (avg, var)
41 }
42
43 #[must_use]
44 pub fn scaled(&self, dc_quality: f32, ac_quality: f32) -> Self {
45 let dc_scaling = Self::quality_scaling(dc_quality);
46 let ac_scaling = Self::quality_scaling(ac_quality);
47
48 let mut out = [0; 64];
49 {
50 debug_assert_eq!(self.coeffs.len(), out.len());
51 debug_assert!(low_weights.len() < self.coeffs.len());
52
53 let (low_coefs, high_coefs) = self.coeffs.split_at(low_weights.len());
54 let (low_out, high_out) = out.split_at_mut(low_weights.len());
55
56 for ((out, coef), w) in low_out.iter_mut().zip(low_coefs).zip(&low_weights) {
58 *out = ((*coef as f32 * (dc_scaling * w + ac_scaling * (1.-w))).round() as Coef).clamp(1, 255);
59 }
60 for (out, coef) in high_out.iter_mut().zip(high_coefs) {
61 *out = ((*coef as f32 * ac_scaling).round() as Coef).clamp(1, 255);
62 }
63 }
64 Self { coeffs: out }
65 }
66
67 #[must_use]
68 pub fn as_ptr(&self) -> *const c_uint {
69 self.coeffs.as_ptr()
70 }
71
72 fn quality_scaling(quality: f32) -> f32 {
74 assert!(quality > 0. && quality <= 100.);
75
76 if quality < 50. {
77 50. / quality
78 } else {
79 (100. - quality) / 50.
80 }
81 }
82}
83
84pub static AnnexK_Luma: QTable = QTable {
85 coeffs: [
86 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69,
87 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81,
88 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99,
89 ],
90};
91
92pub static AnnexK_Chroma: QTable = QTable {
93 coeffs: [
94 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99,
95 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
96 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
97 ],
98};
99
100pub static Flat: QTable = QTable {
101 coeffs: [
102 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
103 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
104 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
105 ],
106};
107
108pub static MSSSIM_Luma: QTable = QTable {
109 coeffs: [
110 12, 17, 20, 21, 30, 34, 56, 63, 18, 20, 20, 26, 28, 51, 61, 55, 19, 20, 21, 26, 33, 58, 69,
111 55, 26, 26, 26, 30, 46, 87, 86, 66, 31, 33, 36, 40, 46, 96, 100, 73, 40, 35, 46, 62, 81,
112 100, 111, 91, 46, 66, 76, 86, 102, 121, 120, 101, 68, 90, 90, 96, 113, 102, 105, 103,
113 ],
114};
115
116pub static MSSSIM_Chroma: QTable = QTable {
117 coeffs: [
118 8, 12, 15, 15, 86, 96, 96, 98, 13, 13, 15, 26, 90, 96, 99, 98, 12, 15, 18, 96, 99, 99, 99,
119 99, 17, 16, 90, 96, 99, 99, 99, 99, 96, 96, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
120 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
121 ],
122};
123
124pub static NRobidoux: QTable = QTable {
125 coeffs: [
126 16, 16, 16, 18, 25, 37, 56, 85, 16, 17, 20, 27, 34, 40, 53, 75, 16, 20, 24, 31, 43, 62, 91,
127 135, 18, 27, 31, 40, 53, 74, 106, 156, 25, 34, 43, 53, 69, 94, 131, 189, 37, 40, 62, 74,
128 94, 124, 169, 238, 56, 53, 91, 106, 131, 169, 226, 311, 85, 75, 135, 156, 189, 238, 311,
129 418,
130 ],
131};
132
133pub static PSNRHVS_Luma: QTable = QTable {
134 coeffs: [
135 9, 10, 12, 14, 27, 32, 51, 62, 11, 12, 14, 19, 27, 44, 59, 73, 12, 14, 18, 25, 42, 59, 79,
136 78, 17, 18, 25, 42, 61, 92, 87, 92, 23, 28, 42, 75, 79, 112, 112, 99, 40, 42, 59, 84, 88,
137 124, 132, 111, 42, 64, 78, 95, 105, 126, 125, 99, 70, 75, 100, 102, 116, 100, 107, 98,
138 ],
139};
140pub static PSNRHVS_Chroma: QTable = QTable {
141 coeffs: [
142 9, 10, 17, 19, 62, 89, 91, 97, 12, 13, 18, 29, 84, 91, 88, 98, 14, 19, 29, 93, 95, 95, 98,
143 97, 20, 26, 84, 88, 95, 95, 98, 94, 26, 86, 91, 93, 97, 99, 98, 99, 99, 100, 98, 99, 99,
144 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 97, 97, 99, 99, 99, 99, 97, 99,
145 ],
146};
147
148pub static KleinSilversteinCarney: QTable = QTable {
149 coeffs: [
150 10, 12, 14, 19, 26, 38, 57, 86, 12, 18, 21, 28, 35, 41, 54, 76, 14, 21, 25, 32, 44, 63, 92,
153 136, 19, 28, 32, 41, 54, 75, 107, 157, 26, 35, 44, 54, 70, 95, 132, 190, 38, 41, 63, 75,
154 95, 125, 170, 239, 57, 54, 92, 107, 132, 170, 227, 312, 86, 76, 136, 157, 190, 239, 312,
155 419,
156 ],
157};
158
159pub static WatsonTaylorBorthwick: QTable = QTable {
160 coeffs: [
161 7, 8, 10, 14, 23, 44, 95, 241, 8, 8, 11, 15, 25, 47, 102, 255, 10, 11, 13, 19, 31, 58, 127,
164 255, 14, 15, 19, 27, 44, 83, 181, 255, 23, 25, 31, 44, 72, 136, 255, 255, 44, 47, 58, 83,
165 136, 255, 255, 255, 95, 102, 127, 181, 255, 255, 255, 255, 241, 255, 255, 255, 255, 255,
166 255, 255,
167 ],
168};
169
170pub static AhumadaWatsonPeterson: QTable = QTable {
171 coeffs: [
172 15, 11, 11, 12, 15, 19, 25, 32, 11, 13, 10, 10, 12, 15, 19, 24, 11, 10, 14, 14, 16, 18, 22,
175 27, 12, 10, 14, 18, 21, 24, 28, 33, 15, 12, 16, 21, 26, 31, 36, 42, 19, 15, 18, 24, 31, 38,
176 45, 53, 25, 19, 22, 28, 36, 45, 55, 65, 32, 24, 27, 33, 42, 53, 65, 77,
177 ],
178};
179
180pub static PetersonAhumadaWatson: QTable = QTable {
181 coeffs: [
182 14, 10, 11, 14, 19, 25, 34, 45, 10, 11, 11, 12, 15, 20, 26, 33, 11, 11, 15, 18, 21, 25, 31,
185 38, 14, 12, 18, 24, 28, 33, 39, 47, 19, 15, 21, 28, 36, 43, 51, 59, 25, 20, 25, 33, 43, 54,
186 64, 74, 34, 26, 31, 39, 51, 64, 77, 91, 45, 33, 38, 47, 59, 74, 91, 108,
187 ],
188};
189
190pub static ALL_TABLES: [(&str, &QTable); 12] = [
191 ("Annex-K Luma", &AnnexK_Luma),
192 ("Annex-K Chroma", &AnnexK_Chroma),
193 ("Flat", &Flat),
194 ("MSSSIM Luma", &MSSSIM_Luma),
195 ("MSSSIM Chroma", &MSSSIM_Chroma),
196 ("N. Robidoux", &NRobidoux),
197 ("PSNRHVS Luma", &PSNRHVS_Luma),
198 ("PSNRHVS Chroma", &PSNRHVS_Chroma),
199 ("Klein, Silverstein, Carney", &KleinSilversteinCarney),
200 ("Watson, Taylor, Borthwick", &WatsonTaylorBorthwick),
201 ("Ahumada, Watson, Peterson", &AhumadaWatsonPeterson),
202 ("Peterson, Ahumada, Watson", &PetersonAhumadaWatson),
203];
204
205#[test]
206fn scaling() {
207 assert_eq!(QTable { coeffs: [100; 64] }, QTable { coeffs: [100; 64] });
208 assert!(QTable { coeffs: [1; 64] } != QTable { coeffs: [2; 64] });
209
210 assert_eq!(QTable{coeffs:[36; 64]}, Flat.scaled(22.,22.));
211 assert_eq!(QTable{coeffs:[8; 64]}, Flat.scaled(75.,75.));
212 assert_eq!(QTable{coeffs:[1; 64]}, Flat.scaled(100.,100.));
213 assert_eq!(QTable{coeffs:[2; 64]}, Flat.scaled(95.,95.));
214 assert_eq!(QTable{coeffs:[
215 2, 6, 15, 32, 32, 32, 32, 32,
216 6, 9, 29, 32, 32, 32, 32, 32,
217 15, 29, 30, 32, 32, 32, 32, 32,
218 32, 32, 32, 32, 32, 32, 32, 32,
219 32, 32, 32, 32, 32, 32, 32, 32,
220 32, 32, 32, 32, 32, 32, 32, 32,
221 32, 32, 32, 32, 32, 32, 32, 32,
222 32, 32, 32, 32, 32, 32, 32, 32]}, Flat.scaled(95.,25.));
223 assert_eq!(PetersonAhumadaWatson, PetersonAhumadaWatson.scaled(50.,50.));
224
225 assert_eq!(QTable { coeffs: [1; 64] }, NRobidoux.scaled(99.9, 99.9));
226 assert_eq!(QTable { coeffs: [1; 64] }, MSSSIM_Chroma.scaled(99.8, 99.8));
227}