use std::collections::HashMap;
use ndarray::{
Array2, Array3, ArrayBase, ArrayView1, ArrayView2, AsArray, Axis, Ix1, Ix3, ViewRepr, Zip, s,
stack,
};
use rayon::prelude::*;
use crate::integration::midpoint;
use crate::parameter::omega;
use crate::prelude::*;
pub fn gs_image<'a, T, A>(
data: A,
period: f64,
mask: Option<ArrayView2<bool>>,
harmonic: Option<f64>,
axis: Option<usize>,
threads: Option<usize>,
) -> Result<Array3<f64>, ImgalError>
where
A: AsArray<'a, T, Ix3>,
T: 'a + AsNumeric,
{
let axis = axis.unwrap_or(2);
if axis >= 3 {
return Err(ImgalError::InvalidAxis {
axis_idx: axis,
dim_len: 3,
});
}
let data: ArrayBase<ViewRepr<&'a T>, Ix3> = data.into();
let h = harmonic.unwrap_or(1.0);
let w = omega(period);
let n: usize = data.len_of(Axis(axis));
let dt: f64 = period / n as f64;
let h_w_dt: f64 = h * w * dt;
let mut w_cos_buf: Vec<f64> = Vec::with_capacity(n);
let mut w_sin_buf: Vec<f64> = Vec::with_capacity(n);
let mut shape = data.shape().to_vec();
shape.remove(axis);
let mut g_arr = Array2::<f64>::zeros((shape[0], shape[1]));
let mut s_arr = Array2::<f64>::zeros((shape[0], shape[1]));
for i in 0..n {
w_cos_buf.push(f64::cos(h_w_dt * (i as f64)));
w_sin_buf.push(f64::sin(h_w_dt * (i as f64)));
}
let lanes = data.lanes(Axis(axis));
let gs_calc = |ln: ArrayView1<T>, g: &mut f64, s: &mut f64| {
let mut iv = 0.0;
let mut gv = 0.0;
let mut sv = 0.0;
ln.iter()
.zip(w_cos_buf.iter())
.zip(w_sin_buf.iter())
.for_each(|((v, cosv), sinv)| {
let vf: f64 = (*v).to_f64();
iv += vf;
gv += vf * cosv;
sv += vf * sinv;
});
iv *= dt;
gv *= dt;
sv *= dt;
*g = gv / iv;
*s = sv / iv;
};
let gs_msk_calc = |ln: ArrayView1<T>, m: &bool, g: &mut f64, s: &mut f64| {
if *m {
let mut iv = 0.0;
let mut gv = 0.0;
let mut sv = 0.0;
ln.iter()
.zip(w_cos_buf.iter())
.zip(w_sin_buf.iter())
.for_each(|((v, cosv), sinv)| {
let vf: f64 = (*v).to_f64();
iv += vf;
gv += vf * cosv;
sv += vf * sinv;
});
iv *= dt;
gv *= dt;
sv *= dt;
*g = gv / iv;
*s = sv / iv;
} else {
*g = 0.0;
*s = 0.0;
}
};
if let Some(msk) = mask {
par!(threads,
seq_exp: Zip::from(lanes).and(msk).and(&mut g_arr).and(&mut s_arr)
.for_each(&gs_msk_calc),
par_exp: Zip::from(lanes).and(msk).and(&mut g_arr).and(&mut s_arr)
.par_for_each(&gs_msk_calc));
} else {
par!(threads,
seq_exp: Zip::from(lanes).and(&mut g_arr).and(&mut s_arr)
.for_each(&gs_calc),
par_exp: Zip::from(lanes).and(&mut g_arr).and(&mut s_arr)
.par_for_each(&gs_calc));
}
Ok(stack(Axis(2), &[g_arr.view(), s_arr.view()]).unwrap())
}
pub fn gs_roi<'a, T, A>(
data: A,
period: f64,
rois: &HashMap<u64, Array2<usize>>,
harmonic: Option<f64>,
axis: Option<usize>,
threads: Option<usize>,
) -> Result<HashMap<u64, Array2<f64>>, ImgalError>
where
A: AsArray<'a, T, Ix3>,
T: 'a + AsNumeric,
{
let data: ArrayBase<ViewRepr<&'a T>, Ix3> = data.into();
let axis = axis.unwrap_or(2);
if axis >= 3 {
return Err(ImgalError::InvalidAxis {
axis_idx: axis,
dim_len: 3,
});
}
let vec_to_arr = |k: u64, v: Vec<Vec<f64>>| {
let arr = Array2::from_shape_vec((v.len(), v[0].len()), v.into_iter().flatten().collect())
.expect("Failed to reshape ROI point cloud into an Array2<f64>.");
(k, arr)
};
let roi_gs_calc_seq = || {
let mut cloud_map: HashMap<u64, Vec<Vec<f64>>> = HashMap::new();
rois.iter().for_each(|(&k, v)| {
let roi_coords = v.lanes(Axis(1));
roi_coords.into_iter().for_each(|p| {
let row = p[0];
let col = p[1];
let ln = match axis {
0 => data.slice(s![.., row, col]),
1 => data.slice(s![row, .., col]),
_ => data.slice(s![row, col, ..]),
};
let g = real_coord(ln, period, harmonic, None);
let s = imaginary_coord(ln, period, harmonic, None);
cloud_map.entry(k).or_default().push(vec![g, s]);
});
});
cloud_map
};
let roi_gs_calc_par = || {
rois.into_par_iter()
.fold(
HashMap::new,
|mut map: HashMap<u64, Vec<Vec<f64>>>, (&k, v)| {
let roi_coords = v.lanes(Axis(1));
roi_coords.into_iter().for_each(|p| {
let row = p[0];
let col = p[1];
let ln = match axis {
0 => data.slice(s![.., row, col]),
1 => data.slice(s![row, .., col]),
_ => data.slice(s![row, col, ..]),
};
let g = real_coord(ln, period, harmonic, None);
let s = imaginary_coord(ln, period, harmonic, None);
map.entry(k).or_default().push(vec![g, s]);
});
map
},
)
.reduce(HashMap::new, |mut map_a, map_b| {
map_b.into_iter().for_each(|(k, mut v)| {
map_a.entry(k).or_insert_with(Vec::new).append(&mut v);
});
map_a
})
};
let cloud_map = par!(threads,
seq_exp: roi_gs_calc_seq(),
par_exp: roi_gs_calc_par());
Ok(cloud_map
.into_iter()
.map(|(k, v)| vec_to_arr(k, v))
.collect())
}
#[inline]
pub fn imaginary_coord<'a, T, A>(
data: A,
period: f64,
harmonic: Option<f64>,
threads: Option<usize>,
) -> f64
where
A: AsArray<'a, T, Ix1>,
T: 'a + AsNumeric,
{
let data: ArrayBase<ViewRepr<&'a T>, Ix1> = data.into();
let h = harmonic.unwrap_or(1.0);
let w = omega(period);
let n = data.len();
let dt = period / (n as f64);
let h_w_dt = h * w * dt;
let buf: Vec<f64> = (0..n)
.map(|i| data[i].to_f64() * (h_w_dt * i as f64).sin())
.collect();
midpoint(&buf, Some(dt), threads) / midpoint(data, Some(dt), threads)
}
#[inline]
pub fn real_coord<'a, T, A>(
data: A,
period: f64,
harmonic: Option<f64>,
threads: Option<usize>,
) -> f64
where
A: AsArray<'a, T, Ix1>,
T: 'a + AsNumeric,
{
let data: ArrayBase<ViewRepr<&'a T>, Ix1> = data.into();
let h = harmonic.unwrap_or(1.0);
let w = omega(period);
let n = data.len();
let dt = period / (n as f64);
let h_w_dt = h * w * dt;
let buf: Vec<f64> = (0..n)
.map(|i| data[i].to_f64() * (h_w_dt * i as f64).cos())
.collect();
midpoint(&buf, Some(dt), threads) / midpoint(data, Some(dt), threads)
}