use nalgebra::{
allocator::Allocator, DefaultAllocator, Dim, IsContiguous, Matrix, RawStorage, RawStorageMut,
ReshapableStorage, Scalar, Storage,
};
use rustfft::{num_complex::Complex, FftDirection, FftPlanner};
pub fn fft_2d<R: Dim, C: Dim, S1, S2>(
mat: Matrix<Complex<f64>, R, C, S1>,
) -> Matrix<Complex<f64>, C, R, S2>
where
S1: IsContiguous + RawStorageMut<Complex<f64>, R, C>,
DefaultAllocator: Allocator<Complex<f64>, C, R>,
S1: Storage<Complex<f64>, R, C> + ReshapableStorage<Complex<f64>, R, C, C, R, Output = S2>,
{
fft_2d_with_direction(mat, FftDirection::Forward)
}
pub fn ifft_2d<R: Dim, C: Dim, S1, S2>(
mat: Matrix<Complex<f64>, R, C, S1>,
) -> Matrix<Complex<f64>, C, R, S2>
where
S1: IsContiguous + RawStorageMut<Complex<f64>, R, C>,
DefaultAllocator: Allocator<Complex<f64>, C, R>,
S1: Storage<Complex<f64>, R, C> + ReshapableStorage<Complex<f64>, R, C, C, R, Output = S2>,
{
fft_2d_with_direction(mat, FftDirection::Inverse)
}
fn fft_2d_with_direction<R: Dim, C: Dim, S1, S2>(
mat: Matrix<Complex<f64>, R, C, S1>,
direction: FftDirection,
) -> Matrix<Complex<f64>, C, R, S2>
where
S1: IsContiguous + RawStorageMut<Complex<f64>, R, C>, DefaultAllocator: Allocator<Complex<f64>, C, R>, S1: Storage<Complex<f64>, R, C> + ReshapableStorage<Complex<f64>, R, C, C, R, Output = S2>, {
let mut mat = mat;
let mut planner = FftPlanner::new();
let (height, width) = mat.shape();
let fft_dim1 = planner.plan_fft(height, direction);
let mut scratch = vec![Complex::default(); fft_dim1.get_inplace_scratch_len()];
for col_buffer in mat.as_mut_slice().chunks_exact_mut(height) {
fft_dim1.process_with_scratch(col_buffer, &mut scratch);
}
let mut transposed = mat.transpose();
let fft_dim2 = planner.plan_fft(width, direction);
scratch.resize(fft_dim2.get_outofplace_scratch_len(), Complex::default());
for (tr_buf, row_buffer) in transposed
.as_mut_slice()
.chunks_exact_mut(width)
.zip(mat.as_mut_slice().chunks_exact_mut(width))
{
fft_dim2.process_outofplace_with_scratch(tr_buf, row_buffer, &mut scratch);
}
mat.reshape_generic(Dim::from_usize(width), Dim::from_usize(height))
}
pub fn ifftshift<T: Scalar, R: Dim, C: Dim, S>(mat: &Matrix<T, R, C, S>) -> Matrix<T, R, C, S>
where
S: Clone + RawStorage<T, R, C> + RawStorageMut<T, R, C>,
{
let is_even = |length| length % 2 == 0;
let (height, width) = mat.shape();
assert!(is_even(width), "Need a dedicated implementation");
assert!(is_even(height), "Need a dedicated implementation");
fftshift(mat)
}
pub fn fftshift<T: Scalar, R: Dim, C: Dim, S>(mat: &Matrix<T, R, C, S>) -> Matrix<T, R, C, S>
where
S: Clone + RawStorage<T, R, C> + RawStorageMut<T, R, C>,
{
let mut shifted: Matrix<T, R, C, S> = mat.clone();
let (height, width) = mat.shape();
let half_width = width / 2;
let half_height = height / 2;
let mat_top_left = mat.slice_range(0..half_height, 0..half_width);
let mat_top_right = mat.slice_range(0..half_height, half_width..);
let mat_bottom_left = mat.slice_range(half_height.., 0..half_width);
let mat_bottom_right = mat.slice_range(half_height.., half_width..);
let mut shifted_bottom_right =
shifted.slice_range_mut(height - half_height..height, width - half_width..width);
shifted_bottom_right.copy_from(&mat_top_left);
let mut shifted_bottom_left =
shifted.slice_range_mut(height - half_height..height, 0..width - half_width);
shifted_bottom_left.copy_from(&mat_top_right);
let mut shifted_top_right =
shifted.slice_range_mut(0..height - half_height, width - half_width..width);
shifted_top_right.copy_from(&mat_bottom_left);
let mut shifted_top_left =
shifted.slice_range_mut(0..height - half_height, 0..width - half_width);
shifted_top_left.copy_from(&mat_bottom_right);
shifted
}
#[cfg(feature = "rustdct")]
pub mod dcst {
use super::*;
use rustdct::DctPlanner;
pub fn dct_2d<R: Dim, C: Dim, S1, S2>(mat: Matrix<f64, R, C, S1>) -> Matrix<f64, C, R, S2>
where
S1: IsContiguous + RawStorageMut<f64, R, C>, DefaultAllocator: Allocator<f64, C, R>, S1: Storage<f64, R, C> + ReshapableStorage<f64, R, C, C, R, Output = S2>, {
let mut mat = mat;
let (height, width) = mat.shape();
let mut planner = DctPlanner::new();
let dct_dim1 = planner.plan_dct2(height);
let mut scratch = vec![0.0; dct_dim1.get_scratch_len()];
for buffer_dim1 in mat.as_mut_slice().chunks_exact_mut(height) {
dct_dim1.process_dct2_with_scratch(buffer_dim1, &mut scratch);
}
let mut transposed = mat.transpose();
let dct_dim2 = planner.plan_dct2(width);
scratch.resize(dct_dim2.get_scratch_len(), 0.0);
for buffer_dim2 in transposed.as_mut_slice().chunks_exact_mut(width) {
dct_dim2.process_dct2_with_scratch(buffer_dim2, &mut scratch);
}
mat.copy_from_slice(transposed.as_slice());
mat.reshape_generic(Dim::from_usize(width), Dim::from_usize(height))
}
pub fn idct_2d<R: Dim, C: Dim, S1, S2>(mat: Matrix<f64, R, C, S1>) -> Matrix<f64, C, R, S2>
where
S1: IsContiguous + RawStorageMut<f64, R, C>, DefaultAllocator: Allocator<f64, C, R>, S1: Storage<f64, R, C> + ReshapableStorage<f64, R, C, C, R, Output = S2>, {
let mut mat = mat;
let (height, width) = mat.shape();
let mut planner = DctPlanner::new();
let dct_dim1 = planner.plan_dct3(height);
let mut scratch = vec![0.0; dct_dim1.get_scratch_len()];
for buffer_dim1 in mat.as_mut_slice().chunks_exact_mut(height) {
dct_dim1.process_dct3_with_scratch(buffer_dim1, &mut scratch);
}
let mut transposed = mat.transpose();
let dct_dim2 = planner.plan_dct3(width);
scratch.resize(dct_dim2.get_scratch_len(), 0.0);
for buffer_dim2 in transposed.as_mut_slice().chunks_exact_mut(width) {
dct_dim2.process_dct3_with_scratch(buffer_dim2, &mut scratch);
}
mat.copy_from_slice(transposed.as_slice());
mat.reshape_generic(Dim::from_usize(width), Dim::from_usize(height))
}
}