1 #![allow(non_upper_case_globals)]
2
3 use std::cmp::{max, min};
4 use std::fmt;
5 use std::os::raw::c_uint;
6 type Coef = c_uint;
7
8 pub struct QTable {
9 pub(crate) coeffs: [Coef; 64],
10 }
11
12 impl PartialEq for QTable {
eq(&self, other: &Self) -> bool13 fn eq(&self, other: &Self) -> bool {
14 let iter2 = (&other.coeffs).iter().cloned();
15 (&self.coeffs).iter().cloned().zip(iter2).all(|(s,o)|s==o)
16 }
17 }
18
19 impl fmt::Debug for QTable {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>20 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
21 write!(fmt, "QTable{{coeffs:{:?}}}", &self.coeffs[..])
22 }
23 }
24
25 const 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
31 impl QTable {
compare(&self, other: &Self) -> (f32, f32)32 pub fn compare(&self, other: &Self) -> (f32, f32) {
33 let mut scales = [0.; 64];
34 for (s, (&a, &b)) in scales.iter_mut().zip(self.coeffs.iter().zip(other.coeffs.iter())) {
35 *s = if b > 0 {a as f32 / b as f32} else {0.};
36 }
37 let avg = scales.iter().sum::<f32>() / 64.;
38 let var = scales.iter().map(|&v| (v - avg).powi(2)).sum::<f32>() / 64.;
39 (avg, var)
40 }
41
42 #[must_use]
scaled(&self, dc_quality: f32, ac_quality: f32) -> Self43 pub fn scaled(&self, dc_quality: f32, ac_quality: f32) -> Self {
44 let dc_scaling = Self::quality_scaling(dc_quality);
45 let ac_scaling = Self::quality_scaling(ac_quality);
46
47 let mut out = [0; 64];
48 {
49 debug_assert_eq!(self.coeffs.len(), out.len());
50 debug_assert!(low_weights.len() < self.coeffs.len());
51
52 let (low_coefs, high_coefs) = self.coeffs.split_at(low_weights.len());
53 let (low_out, high_out) = out.split_at_mut(low_weights.len());
54
55 // TODO: that could be improved for 1x2 and 2x1 subsampling
56 for ((out, coef), w) in low_out.iter_mut().zip(low_coefs).zip(&low_weights) {
57 *out = min(255, max(1, (*coef as f32 * (dc_scaling * w + ac_scaling * (1.-w))).round() as Coef));
58 }
59 for (out, coef) in high_out.iter_mut().zip(high_coefs) {
60 *out = min(255, max(1, (*coef as f32 * ac_scaling).round() as Coef));
61 }
62 }
63 Self { coeffs: out }
64 }
65
as_ptr(&self) -> *const c_uint66 pub fn as_ptr(&self) -> *const c_uint {
67 self.coeffs.as_ptr()
68 }
69
70 // Similar to libjpeg, but result is 100x smaller
quality_scaling(quality: f32) -> f3271 fn quality_scaling(quality: f32) -> f32 {
72 assert!(quality > 0. && quality <= 100.);
73
74 return if quality < 50. {
75 50. / quality
76 } else {
77 (100. - quality) / 50.
78 };
79 }
80 }
81
82 pub static AnnexK_Luma: QTable = QTable{coeffs:[
83 16, 11, 10, 16, 24, 40, 51, 61,
84 12, 12, 14, 19, 26, 58, 60, 55,
85 14, 13, 16, 24, 40, 57, 69, 56,
86 14, 17, 22, 29, 51, 87, 80, 62,
87 18, 22, 37, 56, 68, 109, 103, 77,
88 24, 35, 55, 64, 81, 104, 113, 92,
89 49, 64, 78, 87, 103, 121, 120, 101,
90 72, 92, 95, 98, 112, 100, 103, 99
91 ]};
92
93 pub static AnnexK_Chroma: QTable = QTable{coeffs:[
94 17, 18, 24, 47, 99, 99, 99, 99,
95 18, 21, 26, 66, 99, 99, 99, 99,
96 24, 26, 56, 99, 99, 99, 99, 99,
97 47, 66, 99, 99, 99, 99, 99, 99,
98 99, 99, 99, 99, 99, 99, 99, 99,
99 99, 99, 99, 99, 99, 99, 99, 99,
100 99, 99, 99, 99, 99, 99, 99, 99,
101 99, 99, 99, 99, 99, 99, 99, 99
102 ]};
103
104 pub static Flat: QTable = QTable{coeffs:[
105 16, 16, 16, 16, 16, 16, 16, 16,
106 16, 16, 16, 16, 16, 16, 16, 16,
107 16, 16, 16, 16, 16, 16, 16, 16,
108 16, 16, 16, 16, 16, 16, 16, 16,
109 16, 16, 16, 16, 16, 16, 16, 16,
110 16, 16, 16, 16, 16, 16, 16, 16,
111 16, 16, 16, 16, 16, 16, 16, 16,
112 16, 16, 16, 16, 16, 16, 16, 16
113 ]};
114
115 pub static MSSSIM_Luma: QTable = QTable{coeffs:[
116 12, 17, 20, 21, 30, 34, 56, 63,
117 18, 20, 20, 26, 28, 51, 61, 55,
118 19, 20, 21, 26, 33, 58, 69, 55,
119 26, 26, 26, 30, 46, 87, 86, 66,
120 31, 33, 36, 40, 46, 96, 100, 73,
121 40, 35, 46, 62, 81, 100, 111, 91,
122 46, 66, 76, 86, 102, 121, 120, 101,
123 68, 90, 90, 96, 113, 102, 105, 103
124 ]};
125
126 pub static MSSSIM_Chroma: QTable = QTable{coeffs:[
127 8, 12, 15, 15, 86, 96, 96, 98,
128 13, 13, 15, 26, 90, 96, 99, 98,
129 12, 15, 18, 96, 99, 99, 99, 99,
130 17, 16, 90, 96, 99, 99, 99, 99,
131 96, 96, 99, 99, 99, 99, 99, 99,
132 99, 99, 99, 99, 99, 99, 99, 99,
133 99, 99, 99, 99, 99, 99, 99, 99,
134 99, 99, 99, 99, 99, 99, 99, 99
135 ]};
136
137 pub static NRobidoux: QTable = QTable{coeffs:[
138 16, 16, 16, 18, 25, 37, 56, 85,
139 16, 17, 20, 27, 34, 40, 53, 75,
140 16, 20, 24, 31, 43, 62, 91, 135,
141 18, 27, 31, 40, 53, 74, 106, 156,
142 25, 34, 43, 53, 69, 94, 131, 189,
143 37, 40, 62, 74, 94, 124, 169, 238,
144 56, 53, 91, 106, 131, 169, 226, 311,
145 85, 75, 135, 156, 189, 238, 311, 418
146 ]};
147
148 pub static PSNRHVS_Luma: QTable = QTable{coeffs:[
149 9, 10, 12, 14, 27, 32, 51, 62,
150 11, 12, 14, 19, 27, 44, 59, 73,
151 12, 14, 18, 25, 42, 59, 79, 78,
152 17, 18, 25, 42, 61, 92, 87, 92,
153 23, 28, 42, 75, 79, 112, 112, 99,
154 40, 42, 59, 84, 88, 124, 132, 111,
155 42, 64, 78, 95, 105, 126, 125, 99,
156 70, 75, 100, 102, 116, 100, 107, 98
157 ]};
158 pub static PSNRHVS_Chroma: QTable = QTable{coeffs:[
159 9, 10, 17, 19, 62, 89, 91, 97,
160 12, 13, 18, 29, 84, 91, 88, 98,
161 14, 19, 29, 93, 95, 95, 98, 97,
162 20, 26, 84, 88, 95, 95, 98, 94,
163 26, 86, 91, 93, 97, 99, 98, 99,
164 99, 100, 98, 99, 99, 99, 99, 99,
165 99, 99, 99, 99, 99, 99, 99, 99,
166 97, 97, 99, 99, 99, 99, 97, 99
167 ]};
168
169 pub static KleinSilversteinCarney: QTable = QTable{coeffs:[
170 /* Relevance of human vision to JPEG-DCT compression (1992) Klein, Silverstein and Carney.
171 */
172 10, 12, 14, 19, 26, 38, 57, 86,
173 12, 18, 21, 28, 35, 41, 54, 76,
174 14, 21, 25, 32, 44, 63, 92, 136,
175 19, 28, 32, 41, 54, 75, 107, 157,
176 26, 35, 44, 54, 70, 95, 132, 190,
177 38, 41, 63, 75, 95, 125, 170, 239,
178 57, 54, 92, 107, 132, 170, 227, 312,
179 86, 76, 136, 157, 190, 239, 312, 419
180 ]};
181
182 pub static WatsonTaylorBorthwick: QTable = QTable{coeffs:[
183 /* DCTune perceptual optimization of compressed dental X-Rays (1997) Watson, Taylor, Borthwick
184 */
185 7, 8, 10, 14, 23, 44, 95, 241,
186 8, 8, 11, 15, 25, 47, 102, 255,
187 10, 11, 13, 19, 31, 58, 127, 255,
188 14, 15, 19, 27, 44, 83, 181, 255,
189 23, 25, 31, 44, 72, 136, 255, 255,
190 44, 47, 58, 83, 136, 255, 255, 255,
191 95, 102, 127, 181, 255, 255, 255, 255,
192 241, 255, 255, 255, 255, 255, 255, 255
193 ]};
194
195 pub static AhumadaWatsonPeterson: QTable = QTable{coeffs:[
196 /* A visual detection model for DCT coefficient quantization (12/9/93) Ahumada, Watson, Peterson
197 */
198 15, 11, 11, 12, 15, 19, 25, 32,
199 11, 13, 10, 10, 12, 15, 19, 24,
200 11, 10, 14, 14, 16, 18, 22, 27,
201 12, 10, 14, 18, 21, 24, 28, 33,
202 15, 12, 16, 21, 26, 31, 36, 42,
203 19, 15, 18, 24, 31, 38, 45, 53,
204 25, 19, 22, 28, 36, 45, 55, 65,
205 32, 24, 27, 33, 42, 53, 65, 77
206 ]};
207
208 pub static PetersonAhumadaWatson: QTable = QTable { coeffs:[
209 /* An improved detection model for DCT coefficient quantization (1993) Peterson, Ahumada and Watson
210 */
211 14, 10, 11, 14, 19, 25, 34, 45,
212 10, 11, 11, 12, 15, 20, 26, 33,
213 11, 11, 15, 18, 21, 25, 31, 38,
214 14, 12, 18, 24, 28, 33, 39, 47,
215 19, 15, 21, 28, 36, 43, 51, 59,
216 25, 20, 25, 33, 43, 54, 64, 74,
217 34, 26, 31, 39, 51, 64, 77, 91,
218 45, 33, 38, 47, 59, 74, 91, 108
219 ]};
220
221 pub static ALL_TABLES: [(&str, &QTable); 12] = [
222 ("Annex-K Luma", &AnnexK_Luma),
223 ("Annex-K Chroma", &AnnexK_Chroma),
224 ("Flat", &Flat),
225 ("MSSSIM Luma", &MSSSIM_Luma),
226 ("MSSSIM Chroma", &MSSSIM_Chroma),
227 ("N. Robidoux", &NRobidoux),
228 ("PSNRHVS Luma", &PSNRHVS_Luma),
229 ("PSNRHVS Chroma", &PSNRHVS_Chroma),
230 ("Klein, Silverstein, Carney", &KleinSilversteinCarney),
231 ("Watson, Taylor, Borthwick", &WatsonTaylorBorthwick),
232 ("Ahumada, Watson, Peterson", &AhumadaWatsonPeterson),
233 ("Peterson, Ahumada, Watson", &PetersonAhumadaWatson),
234 ];
235
236 #[test]
scaling()237 fn scaling() {
238 assert_eq!(QTable { coeffs: [100; 64] }, QTable { coeffs: [100; 64] });
239 assert!(QTable { coeffs: [1; 64] } != QTable { coeffs: [2; 64] });
240
241 assert_eq!(QTable{coeffs:[36; 64]}, Flat.scaled(22.,22.));
242 assert_eq!(QTable{coeffs:[8; 64]}, Flat.scaled(75.,75.));
243 assert_eq!(QTable{coeffs:[1; 64]}, Flat.scaled(100.,100.));
244 assert_eq!(QTable{coeffs:[2; 64]}, Flat.scaled(95.,95.));
245 assert_eq!(QTable{coeffs:[
246 2, 6, 15, 32, 32, 32, 32, 32,
247 6, 9, 29, 32, 32, 32, 32, 32,
248 15, 29, 30, 32, 32, 32, 32, 32,
249 32, 32, 32, 32, 32, 32, 32, 32,
250 32, 32, 32, 32, 32, 32, 32, 32,
251 32, 32, 32, 32, 32, 32, 32, 32,
252 32, 32, 32, 32, 32, 32, 32, 32,
253 32, 32, 32, 32, 32, 32, 32, 32]}, Flat.scaled(95.,25.));
254 assert_eq!(PetersonAhumadaWatson, PetersonAhumadaWatson.scaled(50.,50.));
255
256 assert_eq!(QTable { coeffs: [1; 64] }, NRobidoux.scaled(99.9, 99.9));
257 assert_eq!(QTable { coeffs: [1; 64] }, MSSSIM_Chroma.scaled(99.8, 99.8));
258 }
259