use crate::data::{AnyDataArray, DataArray, ImageData};
pub fn image_power_spectrum(input: &ImageData, scalars: &str) -> ImageData {
let arr = match input.point_data().get_array(scalars) {
Some(a) => a,
None => return input.clone(),
};
let dims = input.dimensions();
let n = dims[0] as usize;
if n < 2 { return input.clone(); }
let mut buf = [0.0f64];
let signal: Vec<f64> = (0..n).map(|i| { arr.tuple_as_f64(i, &mut buf); buf[0] }).collect();
let n_out = n / 2 + 1;
let mut power = vec![0.0f64; n_out];
for k in 0..n_out {
let mut re = 0.0;
let mut im = 0.0;
for (j, &x) in signal.iter().enumerate() {
let angle = -2.0 * std::f64::consts::PI * k as f64 * j as f64 / n as f64;
re += x * angle.cos();
im += x * angle.sin();
}
power[k] = (re * re + im * im) / (n as f64 * n as f64);
}
let mut img = ImageData::with_dimensions(n_out, 1, 1);
let spacing = input.spacing();
let freq_spacing = if spacing[0] > 1e-15 { 1.0 / (n as f64 * spacing[0]) } else { 1.0 };
img.set_spacing([freq_spacing, 1.0, 1.0]);
img.point_data_mut().add_array(AnyDataArray::F64(
DataArray::from_vec("PowerSpectrum", power, 1),
));
img
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dc_signal() {
let mut img = ImageData::with_dimensions(8, 1, 1);
img.point_data_mut().add_array(AnyDataArray::F64(
DataArray::from_vec("v", vec![5.0; 8], 1),
));
let result = image_power_spectrum(&img, "v");
let arr = result.point_data().get_array("PowerSpectrum").unwrap();
let mut buf = [0.0f64];
arr.tuple_as_f64(0, &mut buf);
assert!((buf[0] - 25.0).abs() < 1e-10);
arr.tuple_as_f64(1, &mut buf);
assert!(buf[0] < 1e-10);
}
#[test]
fn pure_sine() {
let n = 16;
let mut img = ImageData::with_dimensions(n, 1, 1);
let values: Vec<f64> = (0..n).map(|i| {
(2.0 * std::f64::consts::PI * 2.0 * i as f64 / n as f64).sin()
}).collect();
img.point_data_mut().add_array(AnyDataArray::F64(
DataArray::from_vec("v", values, 1),
));
let result = image_power_spectrum(&img, "v");
let arr = result.point_data().get_array("PowerSpectrum").unwrap();
let mut buf = [0.0f64];
arr.tuple_as_f64(2, &mut buf);
let peak = buf[0];
arr.tuple_as_f64(0, &mut buf);
assert!(peak > buf[0] * 10.0); }
#[test]
fn missing_array() {
let img = ImageData::with_dimensions(4, 1, 1);
let result = image_power_spectrum(&img, "nope");
assert_eq!(result.dimensions(), [4, 1, 1]);
}
}