#[cfg(feature = "image")]
use anyhow::Context;
use anyhow::Result;
#[cfg(feature = "image")]
pub fn save_spectrogram_image(spectrogram: &[Vec<f32>], output_path: &str) -> Result<()> {
use image::{ImageBuffer, Luma};
let n_freq_bins = spectrogram.len();
let n_frames = spectrogram[0].len();
let log_values: Vec<Vec<f32>> = spectrogram
.iter()
.map(|row| row.iter().map(|&v| (v + 1.0).ln()).collect())
.collect();
let min_val = log_values
.iter()
.flatten()
.copied()
.fold(f32::INFINITY, f32::min);
let max_val = log_values
.iter()
.flatten()
.copied()
.fold(f32::NEG_INFINITY, f32::max);
let range = max_val - min_val;
let mut img = ImageBuffer::new(n_frames as u32, n_freq_bins as u32);
for (freq_idx, row) in log_values.iter().enumerate() {
for (time_idx, &value) in row.iter().enumerate() {
let normalized = if range > 0.0 {
((value - min_val) / range * 255.0) as u8
} else {
128u8
};
let y = (n_freq_bins - 1 - freq_idx) as u32;
let x = time_idx as u32;
img.put_pixel(x, y, Luma([normalized]));
}
}
img.save(output_path)
.with_context(|| format!("Failed to save image to {}", output_path))?;
Ok(())
}
#[cfg(not(feature = "image"))]
pub fn save_spectrogram_image(_spectrogram: &[Vec<f32>], _output_path: &str) -> Result<()> {
anyhow::bail!("Image feature not enabled. Compile with --features image to use this function.")
}