#[must_use]
pub fn quantize_subband_97(coeffs: &[f64], step_size: f64, num_bit_planes: u8) -> Vec<i32> {
if (step_size - 1.0).abs() < 1e-10 {
return coeffs
.iter()
.map(|&c| {
let mag = c.abs();
let q = (mag + 0.5).floor();
let qi = q as i32;
if c < 0.0 {
-qi
} else {
qi
}
})
.collect();
}
let scale_inv = (2.0f64).powi(i32::from(num_bit_planes)) / step_size;
coeffs
.iter()
.map(|&c| {
let mag = c.abs() * scale_inv;
let q = (mag + 0.5).floor();
let qi = q as i32;
if c < 0.0 {
-qi
} else {
qi
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::jpeg2000::tier1::CodeBlock;
#[test]
fn quantize_then_dequantize_lossless_shortcut() {
let coeffs = [0.0, 1.7, -3.2, 100.4, -200.6, 255.0];
let q = quantize_subband_97(&coeffs, 1.0, 8);
assert_eq!(q, vec![0, 2, -3, 100, -201, 255]);
let block = CodeBlock {
coeffs: q.clone(),
width: q.len(),
height: 1,
};
let recovered = block.dequantize(1.0, 8);
for (i, (&orig, &rec)) in coeffs.iter().zip(recovered.iter()).enumerate() {
assert!(
(orig - rec).abs() <= 0.5 + 1e-9,
"sample {i}: orig {orig}, recovered {rec}"
);
}
}
#[test]
fn quantize_then_dequantize_general_step_size() {
let nbp = 8u8;
let step_size = 2.0f64;
let coeffs: Vec<f64> = (0..16).map(|i| (i as f64) * 0.01 - 0.07).collect();
let q = quantize_subband_97(&coeffs, step_size, nbp);
let block = CodeBlock {
coeffs: q.clone(),
width: q.len(),
height: 1,
};
let recovered = block.dequantize(step_size, usize::from(nbp));
let max_err = 0.5 * step_size * (0.5f64).powi(i32::from(nbp));
for (i, (&orig, &rec)) in coeffs.iter().zip(recovered.iter()).enumerate() {
assert!(
(orig - rec).abs() <= max_err + 1e-9,
"sample {i}: orig {orig}, rec {rec}, max_err {max_err}"
);
}
}
#[test]
fn quantize_zero_input_yields_zero() {
let coeffs = vec![0.0f64; 5];
let q = quantize_subband_97(&coeffs, 1.0, 8);
assert_eq!(q, vec![0i32; 5]);
let q2 = quantize_subband_97(&coeffs, 2.5, 8);
assert_eq!(q2, vec![0i32; 5]);
}
#[test]
fn quantize_sign_preservation() {
let coeffs = [1.5_f64, -1.5, 7.4, -7.4, 8.0, -8.0];
let q = quantize_subband_97(&coeffs, 1.0, 8);
for (c, qv) in coeffs.iter().zip(q.iter()) {
assert_eq!(c.signum() as i32, qv.signum(), "sign mismatch for {c}");
}
}
}