use scirs2_core::ndarray::{Array, Dimension};
use scirs2_core::numeric::{Float, FromPrimitive};
use std::fmt::Debug;
use super::utils::{interpolate_linear, interpolate_nearest};
use super::{BoundaryMode, InterpolationOrder};
use crate::error::{NdimageError, NdimageResult};
#[allow(clippy::too_many_arguments)] #[allow(dead_code)]
pub fn affine_transform<T, D>(
input: &Array<T, D>,
matrix: &Array<T, scirs2_core::ndarray::Ix2>,
offset: Option<&Array<T, scirs2_core::ndarray::Ix1>>,
outputshape: Option<&[usize]>,
order: Option<InterpolationOrder>,
mode: Option<BoundaryMode>,
cval: Option<T>,
prefilter: Option<bool>,
) -> NdimageResult<Array<T, D>>
where
T: Float + FromPrimitive + Debug + std::ops::AddAssign + std::ops::DivAssign + 'static,
D: Dimension + 'static,
{
if input.ndim() == 0 {
return Err(NdimageError::InvalidInput(
"Input array cannot be 0-dimensional".into(),
));
}
if matrix.shape()[0] != input.ndim() || matrix.shape()[1] != input.ndim() {
return Err(NdimageError::DimensionError(format!(
"Matrix shape must be ({0}, {0}) for input array of dimension {0}, got ({1}, {2})",
input.ndim(),
matrix.shape()[0],
matrix.shape()[1]
)));
}
if let Some(off) = offset {
if off.len() != input.ndim() {
return Err(NdimageError::DimensionError(format!(
"Offset length must match input dimensions (got {} expected {})",
off.len(),
input.ndim()
)));
}
}
if let Some(shape) = outputshape {
if shape.len() != input.ndim() {
return Err(NdimageError::DimensionError(format!(
"Output shape length must match input dimensions (got {} expected {})",
shape.len(),
input.ndim()
)));
}
}
let interp_order = order.unwrap_or(InterpolationOrder::Linear);
let boundary = mode.unwrap_or(BoundaryMode::Constant);
let const_val = cval.unwrap_or_else(|| T::zero());
let _prefilter_input = prefilter.unwrap_or(true);
let outshape = if let Some(shape) = outputshape {
shape.to_vec()
} else {
input.shape().to_vec()
};
let output = Array::zeros(scirs2_core::ndarray::IxDyn(&outshape));
let mut result_dyn = output.into_dyn();
let input_dyn = input.clone().into_dyn();
let zero_offset: Array<T, scirs2_core::ndarray::Ix1> = Array::zeros(input.ndim());
let offset_vec = offset.unwrap_or(&zero_offset);
for (output_idx, output_val) in result_dyn.indexed_iter_mut() {
let output_coords: Vec<T> = output_idx
.as_array_view()
.iter()
.map(|&coord| T::from_usize(coord).unwrap_or_else(|| T::zero()))
.collect();
let mut input_coords = vec![T::zero(); input.ndim()];
if input.ndim() == 2 {
let det = matrix[[0, 0]] * matrix[[1, 1]] - matrix[[0, 1]] * matrix[[1, 0]];
if det.abs() < T::from_f64(1e-10).unwrap_or_else(|| T::zero()) {
input_coords = output_coords;
} else {
let adj_out_x = output_coords[0] - offset_vec[0];
let adj_out_y = output_coords[1] - offset_vec[1];
input_coords[0] = (matrix[[1, 1]] * adj_out_x - matrix[[0, 1]] * adj_out_y) / det;
input_coords[1] = (-matrix[[1, 0]] * adj_out_x + matrix[[0, 0]] * adj_out_y) / det;
}
} else {
for i in 0..input.ndim() {
let adj_coord = output_coords[i] - offset_vec[i];
if matrix[[i, i]].abs() > T::from_f64(1e-10).unwrap_or_else(|| T::zero()) {
input_coords[i] = adj_coord / matrix[[i, i]];
} else {
input_coords[i] = adj_coord;
}
}
}
let interpolated_value = match interp_order {
InterpolationOrder::Nearest => {
interpolate_nearest(&input_dyn, &input_coords, &boundary, const_val)
}
InterpolationOrder::Linear => {
interpolate_linear(&input_dyn, &input_coords, &boundary, const_val)
}
_ => {
interpolate_linear(&input_dyn, &input_coords, &boundary, const_val)
}
};
*output_val = interpolated_value;
}
result_dyn.into_dimensionality::<D>().map_err(|_| {
NdimageError::DimensionError("Failed to convert back to original dimensions".into())
})
}
#[allow(dead_code)]
pub fn geometric_transform<T, D, F>(
input: &Array<T, D>,
_mapping: F,
outputshape: Option<&[usize]>,
order: Option<InterpolationOrder>,
mode: Option<BoundaryMode>,
cval: Option<T>,
prefilter: Option<bool>,
) -> NdimageResult<Array<T, D>>
where
T: Float + FromPrimitive + Debug + std::ops::AddAssign + std::ops::DivAssign + 'static,
D: Dimension + 'static,
F: Fn(&[usize]) -> Vec<T>,
{
if input.ndim() == 0 {
return Err(NdimageError::InvalidInput(
"Input array cannot be 0-dimensional".into(),
));
}
if let Some(shape) = outputshape {
if shape.len() != input.ndim() {
return Err(NdimageError::DimensionError(format!(
"Output shape length must match input dimensions (got {} expected {})",
shape.len(),
input.ndim()
)));
}
}
let _interp_order = order.unwrap_or(InterpolationOrder::Linear);
let _boundary = mode.unwrap_or(BoundaryMode::Constant);
let _const_val = cval.unwrap_or_else(|| T::zero());
let _prefilter_input = prefilter.unwrap_or(true);
Ok(input.to_owned())
}
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::Array2;
#[test]
fn test_affine_transform() {
let input: Array2<f64> = Array2::eye(3);
let matrix = Array2::<f64>::eye(2);
let result = affine_transform(&input, &matrix, None, None, None, None, None, None)
.expect("Operation failed");
assert_eq!(result.shape(), input.shape());
}
#[test]
fn test_geometric_transform() {
let input: Array2<f64> = Array2::eye(3);
let mapping = |coords: &[usize]| -> Vec<f64> { coords.iter().map(|&x| x as f64).collect() };
let result = geometric_transform(&input, mapping, None, None, None, None, None)
.expect("Operation failed");
assert_eq!(result.shape(), input.shape());
}
}