use rustfft::FftDirection;
use rustfft::{num_complex::Complex, FftPlanner};
pub fn fft_2d(width: usize, height: usize, img_buffer: &mut [Complex<f64>]) {
fft_2d_with_direction(width, height, img_buffer, FftDirection::Forward)
}
pub fn ifft_2d(width: usize, height: usize, img_buffer: &mut [Complex<f64>]) {
fft_2d_with_direction(width, height, img_buffer, FftDirection::Inverse)
}
fn fft_2d_with_direction(
width: usize,
height: usize,
img_buffer: &mut [Complex<f64>],
direction: FftDirection,
) {
let mut planner = FftPlanner::new();
let fft_width = planner.plan_fft(width, direction);
let mut scratch = vec![Complex::default(); fft_width.get_inplace_scratch_len()];
for row_buffer in img_buffer.chunks_exact_mut(width) {
fft_width.process_with_scratch(row_buffer, &mut scratch);
}
let mut transposed = transpose(width, height, img_buffer);
let fft_height = planner.plan_fft(height, direction);
scratch.resize(fft_height.get_outofplace_scratch_len(), Complex::default());
for (tr_buf, col_buf) in transposed
.chunks_exact_mut(height)
.zip(img_buffer.chunks_exact_mut(height))
{
fft_height.process_outofplace_with_scratch(tr_buf, col_buf, &mut scratch);
}
}
fn transpose<T: Copy + Default>(width: usize, height: usize, matrix: &[T]) -> Vec<T> {
let mut ind = 0;
let mut ind_tr;
let mut transposed = vec![T::default(); matrix.len()];
for row in 0..height {
ind_tr = row;
for _ in 0..width {
transposed[ind_tr] = matrix[ind];
ind += 1;
ind_tr += height;
}
}
transposed
}
pub fn ifftshift<T: Copy + Default>(width: usize, height: usize, matrix: &[T]) -> Vec<T> {
let is_even = |length| length % 2 == 0;
assert!(is_even(width), "Need a dedicated implementation");
assert!(is_even(height), "Need a dedicated implementation");
fftshift(width, height, matrix)
}
pub fn fftshift<T: Copy + Default>(width: usize, height: usize, matrix: &[T]) -> Vec<T> {
let mut shifted = vec![T::default(); matrix.len()];
let half_width = width / 2;
let half_height = height / 2;
for row in 0..half_height {
let mrow_start = row * width;
let m_row = &matrix[mrow_start..mrow_start + width];
let srow_start = mrow_start + (height - half_height) * width;
let s_row = &mut shifted[srow_start..srow_start + width];
s_row[width - half_width..width].copy_from_slice(&m_row[0..half_width]);
s_row[0..width - half_width].copy_from_slice(&m_row[half_width..width]);
}
for row in half_height..height {
let mrow_start = row * width;
let m_row = &matrix[mrow_start..mrow_start + width];
let srow_start = (row - half_height) * width;
let s_row = &mut shifted[srow_start..srow_start + width];
s_row[width - half_width..width].copy_from_slice(&m_row[0..half_width]);
s_row[0..width - half_width].copy_from_slice(&m_row[half_width..width]);
}
shifted
}
#[cfg(feature = "rustdct")]
pub mod dcst {
use super::transpose;
use rustdct::DctPlanner;
pub fn dct_2d(width: usize, height: usize, img_buffer: &mut [f64]) {
let mut planner = DctPlanner::new();
let dct_width = planner.plan_dct2(width);
let mut scratch = vec![0.0; dct_width.get_scratch_len()];
for row_buffer in img_buffer.chunks_exact_mut(width) {
dct_width.process_dct2_with_scratch(row_buffer, &mut scratch);
}
let mut transposed = transpose(width, height, img_buffer);
let dct_height = planner.plan_dct2(height);
scratch.resize(dct_height.get_scratch_len(), 0.0);
for column_buffer in transposed.chunks_exact_mut(height) {
dct_height.process_dct2_with_scratch(column_buffer, &mut scratch);
}
img_buffer.copy_from_slice(&transposed);
}
pub fn idct_2d(width: usize, height: usize, img_buffer: &mut [f64]) {
let mut planner = DctPlanner::new();
let dct_width = planner.plan_dct3(width);
let mut scratch = vec![0.0; dct_width.get_scratch_len()];
for row_buffer in img_buffer.chunks_exact_mut(width) {
dct_width.process_dct3_with_scratch(row_buffer, &mut scratch);
}
let mut transposed = transpose(width, height, img_buffer);
let dct_height = planner.plan_dct3(height);
scratch.resize(dct_height.get_scratch_len(), 0.0);
for column_buffer in transposed.chunks_exact_mut(height) {
dct_height.process_dct3_with_scratch(column_buffer, &mut scratch);
}
img_buffer.copy_from_slice(&transposed);
}
}