const EPSILON: f32 = 1e-10;
pub fn sharpen_chroma(chroma: &[f32], power: f32) -> Vec<f32> {
if chroma.is_empty() {
return vec![];
}
log::debug!("Sharpening chroma with power {}", power);
let mut sharpened: Vec<f32> = chroma
.iter()
.map(|&x| x.powf(power))
.collect();
let norm: f32 = sharpened.iter().map(|&x| x * x).sum::<f32>().sqrt();
if norm > EPSILON {
for x in &mut sharpened {
*x /= norm;
}
} else {
let uniform = 1.0 / (chroma.len() as f32).sqrt();
sharpened.fill(uniform);
}
sharpened
}
pub fn l2_normalize_chroma(chroma: &[f32]) -> Vec<f32> {
if chroma.is_empty() {
return vec![];
}
let norm: f32 = chroma.iter().map(|&x| x * x).sum::<f32>().sqrt();
if norm > EPSILON {
chroma.iter().map(|&x| x / norm).collect()
} else {
let uniform = 1.0 / (chroma.len() as f32).sqrt();
vec![uniform; chroma.len()]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sharpen_chroma() {
let chroma = vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0];
let sharpened = sharpen_chroma(&chroma, 2.0);
assert_eq!(sharpened.len(), 12);
let norm: f32 = sharpened.iter().map(|&x| x * x).sum::<f32>().sqrt();
assert!((norm - 1.0).abs() < 0.01);
assert!(sharpened[5] > chroma[5] || chroma[5] < 0.1);
}
#[test]
fn test_sharpen_chroma_power_one() {
let chroma = vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0];
let sharpened = sharpen_chroma(&chroma, 1.0);
let norm_orig: f32 = chroma.iter().map(|&x| x * x).sum::<f32>().sqrt();
let normalized_orig: Vec<f32> = chroma.iter().map(|&x| x / norm_orig).collect();
for (a, b) in normalized_orig.iter().zip(sharpened.iter()) {
assert!((a - b).abs() < 0.01);
}
}
#[test]
fn test_sharpen_chroma_empty() {
let result = sharpen_chroma(&[], 2.0);
assert_eq!(result.len(), 0);
}
#[test]
fn test_l2_normalize_chroma() {
let chroma = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0];
let normalized = l2_normalize_chroma(&chroma);
assert_eq!(normalized.len(), 12);
let norm: f32 = normalized.iter().map(|&x| x * x).sum::<f32>().sqrt();
assert!((norm - 1.0).abs() < 0.01);
}
#[test]
fn test_l2_normalize_chroma_empty() {
let result = l2_normalize_chroma(&[]);
assert_eq!(result.len(), 0);
}
#[test]
fn test_l2_normalize_chroma_zero() {
let chroma = vec![0.0; 12];
let normalized = l2_normalize_chroma(&chroma);
let expected = 1.0 / (12.0f32).sqrt();
for &x in &normalized {
assert!((x - expected).abs() < 0.01);
}
}
}