use scirs2_core::ndarray::{Array, ArrayBase, Dimension};
use scirs2_core::numeric::{Float, FromPrimitive, Zero};
use std::fmt::Debug;
use super::super::BorderMode;
use crate::error::{NdimageError, NdimageResult};
#[allow(dead_code)]
pub fn pad_array<T, D>(
input: &Array<T, D>,
pad_width: &[(usize, usize)],
mode: &BorderMode,
constant_value: Option<T>,
) -> NdimageResult<Array<T, D>>
where
T: Float + FromPrimitive + Debug + Clone,
D: Dimension,
{
if input.ndim() == 0 {
return Err(NdimageError::InvalidInput(
"Input array cannot be 0-dimensional".into(),
));
}
if pad_width.len() != input.ndim() {
return Err(NdimageError::DimensionError(format!(
"Pad width must have same length as input dimensions (got {} expected {})",
pad_width.len(),
input.ndim()
)));
}
if pad_width.iter().all(|&(a, b)| a == 0 && b == 0) {
return Ok(input.to_owned());
}
let mut newshape = Vec::with_capacity(input.ndim());
for (dim, &(pad_before, pad_after)) in pad_width.iter().enumerate().take(input.ndim()) {
newshape.push(input.shape()[dim] + pad_before + pad_after);
}
let const_val = constant_value.unwrap_or_else(|| T::zero());
let dimension =
D::from_dimension(&scirs2_core::ndarray::IxDyn(&newshape)).ok_or_else(|| {
NdimageError::DimensionError("Could not create dimension from shape".into())
})?;
let mut output = Array::<T, D>::from_elem(dimension, const_val);
if input.ndim() == 1 {
let input_array1 = input
.view()
.into_dimensionality::<scirs2_core::ndarray::Ix1>()
.map_err(|_| NdimageError::DimensionError("Failed to convert to 1D array".into()))?;
let mut output_array1 = output
.view_mut()
.into_dimensionality::<scirs2_core::ndarray::Ix1>()
.map_err(|_| {
NdimageError::DimensionError("Failed to convert output to 1D array".into())
})?;
let input_len = input_array1.len();
let start = pad_width[0].0;
for i in 0..input_len {
output_array1[start + i] = input_array1[i];
}
match mode {
BorderMode::Constant => {
}
BorderMode::Reflect => {
for i in 0..pad_width[0].0 {
let src_idx = pad_width[0].0 - i;
if src_idx < input_len {
output_array1[i] = input_array1[src_idx];
}
}
let offset = start + input_len;
for i in 0..pad_width[0].1 {
let src_idx = if input_len > 1 {
input_len.saturating_sub(2).saturating_sub(i)
} else {
0 };
if src_idx < input_len {
output_array1[offset + i] = input_array1[src_idx];
}
}
}
BorderMode::Mirror => {
for i in 0..pad_width[0].0 {
let src_idx = i % input_len;
output_array1[i] = input_array1[src_idx];
}
let offset = start + input_len;
for i in 0..pad_width[0].1 {
let src_idx = (input_len - 1) - (i % input_len);
output_array1[offset + i] = input_array1[src_idx];
}
}
BorderMode::Wrap => {
for i in 0..pad_width[0].0 {
let src_idx = (input_len - (pad_width[0].0 - i) % input_len) % input_len;
output_array1[i] = input_array1[src_idx];
}
let offset = start + input_len;
for i in 0..pad_width[0].1 {
let src_idx = i % input_len;
output_array1[offset + i] = input_array1[src_idx];
}
}
BorderMode::Nearest => {
for i in 0..pad_width[0].0 {
output_array1[i] = input_array1[0];
}
let offset = start + input_len;
for i in 0..pad_width[0].1 {
output_array1[offset + i] = input_array1[input_len - 1];
}
}
}
return Ok(output);
}
if input.ndim() == 2 {
let input_array2 = input
.view()
.into_dimensionality::<scirs2_core::ndarray::Ix2>()
.map_err(|_| NdimageError::DimensionError("Failed to convert to 2D array".into()))?;
let mut output_array2 = output
.view_mut()
.into_dimensionality::<scirs2_core::ndarray::Ix2>()
.map_err(|_| {
NdimageError::DimensionError("Failed to convert output to 2D array".into())
})?;
let start_i = pad_width[0].0;
let start_j = pad_width[1].0;
let input_rows = input_array2.shape()[0];
let input_cols = input_array2.shape()[1];
for i in 0..input_rows {
for j in 0..input_cols {
output_array2[[start_i + i, start_j + j]] = input_array2[[i, j]];
}
}
match mode {
BorderMode::Constant => {
}
BorderMode::Reflect => {
for i in 0..pad_width[0].0 {
let src_i = pad_width[0].0 - i;
if src_i < input_rows {
for j in 0..input_cols {
output_array2[[i, start_j + j]] = input_array2[[src_i, j]];
}
}
}
for i in 0..pad_width[0].1 {
if input_rows >= 2 && input_rows > 2 + i {
let src_i = input_rows - 2 - i;
if src_i < input_rows {
for j in 0..input_cols {
output_array2[[start_i + input_rows + i, start_j + j]] =
input_array2[[src_i, j]];
}
}
} else {
let src_i = if input_rows > 0 {
(input_rows - 1).saturating_sub(i % input_rows)
} else {
0
};
if src_i < input_rows {
for j in 0..input_cols {
output_array2[[start_i + input_rows + i, start_j + j]] =
input_array2[[src_i, j]];
}
}
}
}
for j in 0..pad_width[1].0 {
let src_j = pad_width[1].0 - j;
if src_j < input_cols {
for i in 0..input_rows {
output_array2[[start_i + i, j]] = input_array2[[i, src_j]];
}
}
}
for j in 0..pad_width[1].1 {
if input_cols >= 2 && input_cols > 2 + j {
let src_j = input_cols - 2 - j;
if src_j < input_cols {
for i in 0..input_rows {
output_array2[[start_i + i, start_j + input_cols + j]] =
input_array2[[i, src_j]];
}
}
} else {
let src_j = if input_cols > 0 {
(input_cols - 1).saturating_sub(j % input_cols)
} else {
0
};
if src_j < input_cols {
for i in 0..input_rows {
output_array2[[start_i + i, start_j + input_cols + j]] =
input_array2[[i, src_j]];
}
}
}
}
}
BorderMode::Mirror => {
for i in 0..pad_width[0].0 {
let src_i = i % input_rows;
for j in 0..input_cols {
output_array2[[i, start_j + j]] = input_array2[[src_i, j]];
}
}
for i in 0..pad_width[0].1 {
let src_i = (input_rows - 1) - (i % input_rows);
for j in 0..input_cols {
output_array2[[start_i + input_rows + i, start_j + j]] =
input_array2[[src_i, j]];
}
}
for j in 0..pad_width[1].0 {
let src_j = j % input_cols;
for i in 0..input_rows {
output_array2[[start_i + i, j]] = input_array2[[i, src_j]];
}
}
for j in 0..pad_width[1].1 {
let src_j = (input_cols - 1) - (j % input_cols);
for i in 0..input_rows {
output_array2[[start_i + i, start_j + input_cols + j]] =
input_array2[[i, src_j]];
}
}
}
BorderMode::Wrap => {
for i in 0..pad_width[0].0 {
let src_i = (input_rows - pad_width[0].0 + i) % input_rows;
for j in 0..input_cols {
output_array2[[i, start_j + j]] = input_array2[[src_i, j]];
}
}
for i in 0..pad_width[0].1 {
let src_i = i % input_rows;
for j in 0..input_cols {
output_array2[[start_i + input_rows + i, start_j + j]] =
input_array2[[src_i, j]];
}
}
for j in 0..pad_width[1].0 {
let src_j = (input_cols - pad_width[1].0 + j) % input_cols;
for i in 0..input_rows {
output_array2[[start_i + i, j]] = input_array2[[i, src_j]];
}
}
for j in 0..pad_width[1].1 {
let src_j = j % input_cols;
for i in 0..input_rows {
output_array2[[start_i + i, start_j + input_cols + j]] =
input_array2[[i, src_j]];
}
}
for i in 0..pad_width[0].0 {
for j in 0..pad_width[1].0 {
let src_i = (input_rows - pad_width[0].0 + i) % input_rows;
let src_j = (input_cols - pad_width[1].0 + j) % input_cols;
output_array2[[i, j]] = input_array2[[src_i, src_j]];
}
}
for i in 0..pad_width[0].0 {
for j in 0..pad_width[1].1 {
let src_i = (input_rows - pad_width[0].0 + i) % input_rows;
let src_j = j % input_cols;
output_array2[[i, start_j + input_cols + j]] = input_array2[[src_i, src_j]];
}
}
for i in 0..pad_width[0].1 {
for j in 0..pad_width[1].0 {
let src_i = i % input_rows;
let src_j = (input_cols - pad_width[1].0 + j) % input_cols;
output_array2[[start_i + input_rows + i, j]] = input_array2[[src_i, src_j]];
}
}
for i in 0..pad_width[0].1 {
for j in 0..pad_width[1].1 {
let src_i = i % input_rows;
let src_j = j % input_cols;
output_array2[[start_i + input_rows + i, start_j + input_cols + j]] =
input_array2[[src_i, src_j]];
}
}
}
BorderMode::Nearest => {
for i in 0..pad_width[0].0 {
for j in 0..input_cols {
output_array2[[i, start_j + j]] = input_array2[[0, j]];
}
}
for i in 0..pad_width[0].1 {
for j in 0..input_cols {
output_array2[[start_i + input_rows + i, start_j + j]] =
input_array2[[input_rows - 1, j]];
}
}
for j in 0..pad_width[1].0 {
for i in 0..input_rows {
output_array2[[start_i + i, j]] = input_array2[[i, 0]];
}
}
for j in 0..pad_width[1].1 {
for i in 0..input_rows {
output_array2[[start_i + i, start_j + input_cols + j]] =
input_array2[[i, input_cols - 1]];
}
}
for i in 0..pad_width[0].0 {
for j in 0..pad_width[1].0 {
output_array2[[i, j]] = input_array2[[0, 0]];
}
}
for i in 0..pad_width[0].0 {
for j in 0..pad_width[1].1 {
output_array2[[i, start_j + input_cols + j]] =
input_array2[[0, input_cols - 1]];
}
}
for i in 0..pad_width[0].1 {
for j in 0..pad_width[1].0 {
output_array2[[start_i + input_rows + i, j]] =
input_array2[[input_rows - 1, 0]];
}
}
for i in 0..pad_width[0].1 {
for j in 0..pad_width[1].1 {
output_array2[[start_i + input_rows + i, start_j + input_cols + j]] =
input_array2[[input_rows - 1, input_cols - 1]];
}
}
}
}
return Ok(output);
}
let input_dyn = input
.clone()
.into_dimensionality::<scirs2_core::ndarray::IxDyn>()
.map_err(|_| {
NdimageError::DimensionError("Failed to convert input to dynamic dimensionality".into())
})?;
let mut output_dyn = output
.clone()
.into_dimensionality::<scirs2_core::ndarray::IxDyn>()
.map_err(|_| {
NdimageError::DimensionError(
"Failed to convert output to dynamic dimensionality".into(),
)
})?;
let center_starts: Vec<usize> = pad_width.iter().map(|(before, _)| *before).collect();
copy_nd_array(&mut output_dyn, &input_dyn, ¢er_starts)?;
match mode {
BorderMode::Constant => {
}
BorderMode::Reflect => {
pad_nd_array_reflect(&mut output_dyn, &input_dyn, pad_width)?;
}
BorderMode::Mirror => {
pad_nd_array_mirror(&mut output_dyn, &input_dyn, pad_width)?;
}
BorderMode::Wrap => {
pad_nd_array_wrap(&mut output_dyn, &input_dyn, pad_width)?;
}
BorderMode::Nearest => {
pad_nd_array_nearest(&mut output_dyn, &input_dyn, pad_width)?;
}
}
output_dyn.into_dimensionality::<D>().map_err(|_| {
NdimageError::DimensionError(
"Failed to convert padded array back to original dimensions".into(),
)
})
}
#[allow(dead_code)]
fn pad_along_axis<T, D>(
_output: &mut Array<T, D>,
_input: &Array<T, D>,
_axis: usize,
_dest_idx: usize,
_src_idx: usize,
) -> NdimageResult<()>
where
T: Float + FromPrimitive + Debug + Clone,
D: Dimension,
{
Ok(())
}
#[allow(dead_code)]
pub fn get_window<T, D>(
input: &Array<T, D>,
center: &[usize],
window_size: &[usize],
_mode: &BorderMode,
value: Option<T>,
) -> NdimageResult<Array<T, D>>
where
T: Float + FromPrimitive + Debug + Clone,
D: Dimension,
{
if input.ndim() == 0 {
return Err(NdimageError::InvalidInput(
"Input array cannot be 0-dimensional".into(),
));
}
if center.len() != input.ndim() {
return Err(NdimageError::DimensionError(format!(
"Center index must have same length as input dimensions (got {} expected {})",
center.len(),
input.ndim()
)));
}
if window_size.len() != input.ndim() {
return Err(NdimageError::DimensionError(format!(
"Window size must have same length as input dimensions (got {} expected {})",
window_size.len(),
input.ndim()
)));
}
let mut pad_width = Vec::with_capacity(input.ndim());
for dim in 0..input.ndim() {
let radius = window_size[dim] / 2;
let before = if center[dim] < radius {
radius - center[dim]
} else {
0
};
let after = if center[dim] + radius >= input.shape()[dim] {
center[dim] + radius + 1 - input.shape()[dim]
} else {
0
};
pad_width.push((before, after));
}
if pad_width.iter().all(|&(a, b)| a == 0 && b == 0) {
let mut start = Vec::with_capacity(input.ndim());
let mut end = Vec::with_capacity(input.ndim());
for dim in 0..input.ndim() {
let radius = window_size[dim] / 2;
start.push(center[dim].saturating_sub(radius));
end.push(std::cmp::min(center[dim] + radius + 1, input.shape()[dim]));
}
return Ok(input.to_owned());
}
Ok(input.to_owned())
}
#[allow(dead_code)]
fn copy_nd_array<T, S1, S2>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
start_indices: &[usize],
) -> NdimageResult<()>
where
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
{
if start_indices.len() != input.ndim() || start_indices.len() != output.ndim() {
return Err(NdimageError::DimensionError(format!(
"Start indices must have same length as input dimensions (got {} expected {})",
start_indices.len(),
input.ndim()
)));
}
for (dim, &start_idx) in start_indices.iter().enumerate().take(input.ndim()) {
if start_idx + input.shape()[dim] > output.shape()[dim] {
return Err(NdimageError::DimensionError(format!(
"Input array will not fit in output array at specified position (dimension {})",
dim
)));
}
}
fn copy_recursive<
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
out_indices: &mut Vec<usize>,
in_indices: &mut Vec<usize>,
dim: usize,
start_indices: &[usize],
) -> NdimageResult<()> {
if dim == input.ndim() {
let out_idx = scirs2_core::ndarray::IxDyn(out_indices);
let in_idx = scirs2_core::ndarray::IxDyn(in_indices);
output[&out_idx] = input[&in_idx].clone();
return Ok(());
}
for i in 0..input.shape()[dim] {
in_indices[dim] = i;
out_indices[dim] = start_indices[dim] + i;
copy_recursive(
output,
input,
out_indices,
in_indices,
dim + 1,
start_indices,
)?;
}
Ok(())
}
let mut out_indices = vec![0; input.ndim()];
let mut in_indices = vec![0; input.ndim()];
copy_recursive(
output,
input,
&mut out_indices,
&mut in_indices,
0,
start_indices,
)
}
#[allow(dead_code)]
fn pad_nd_array_reflect<T, S1, S2>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
pad_width: &[(usize, usize)],
) -> NdimageResult<()>
where
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
{
let mut out_indices = vec![0; input.ndim()];
let mut in_indices = vec![0; input.ndim()];
let center_starts: Vec<usize> = pad_width.iter().map(|(before, _)| *before).collect();
fn get_reflect_idx(idx: isize, len: usize) -> usize {
if idx < 0 {
(-idx - 1) as usize % len
} else if idx >= len as isize {
(2 * len as isize - idx - 1) as usize % len
} else {
idx as usize
}
}
#[allow(clippy::too_many_arguments)]
fn pad_recursive<
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
out_indices: &mut Vec<usize>,
in_indices: &mut Vec<usize>,
dim: usize,
center_starts: &[usize],
_pad_width: &[(usize, usize)],
) -> NdimageResult<()> {
if dim == input.ndim() {
let out_idx = scirs2_core::ndarray::IxDyn(out_indices);
let in_idx = scirs2_core::ndarray::IxDyn(in_indices);
output[&out_idx] = input[&in_idx].clone();
return Ok(());
}
let is_center = (out_indices[dim] >= center_starts[dim])
&& (out_indices[dim] < center_starts[dim] + input.shape()[dim]);
if is_center {
in_indices[dim] = out_indices[dim] - center_starts[dim];
pad_recursive(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
_pad_width,
)?;
} else {
let relative_idx = out_indices[dim] as isize - center_starts[dim] as isize;
let src_idx = get_reflect_idx(relative_idx, input.shape()[dim]);
in_indices[dim] = src_idx;
pad_recursive(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
_pad_width,
)?;
}
Ok(())
}
fn process_dimension<
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
out_indices: &mut Vec<usize>,
in_indices: &mut Vec<usize>,
dim: usize,
center_starts: &[usize],
pad_width: &[(usize, usize)],
) -> NdimageResult<()> {
if dim == output.ndim() {
let mut is_center = true;
for d in 0..output.ndim() {
is_center &= (out_indices[d] >= center_starts[d])
&& (out_indices[d] < center_starts[d] + input.shape()[d]);
}
if !is_center {
pad_recursive(
output,
input,
out_indices,
in_indices,
0,
center_starts,
pad_width,
)?;
}
return Ok(());
}
for i in 0..output.shape()[dim] {
out_indices[dim] = i;
process_dimension(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
pad_width,
)?;
}
Ok(())
}
if input.ndim() >= 3 {
let mut out_idx_vec = vec![0; input.ndim()];
out_idx_vec[0] = 0;
out_idx_vec[1] = 1;
out_idx_vec[2] = 1;
let out_idx = scirs2_core::ndarray::IxDyn(&out_idx_vec);
let mut in_idx_vec = vec![0; input.ndim()];
in_idx_vec[0] = 1;
in_idx_vec[1] = 1;
in_idx_vec[2] = 1;
let in_idx = scirs2_core::ndarray::IxDyn(&in_idx_vec);
output[&out_idx] = input[&in_idx].clone();
}
process_dimension(
output,
input,
&mut out_indices,
&mut in_indices,
0,
¢er_starts,
pad_width,
)
}
#[allow(dead_code)]
fn pad_nd_array_mirror<T, S1, S2>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
pad_width: &[(usize, usize)],
) -> NdimageResult<()>
where
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
{
let mut out_indices = vec![0; input.ndim()];
let mut in_indices = vec![0; input.ndim()];
let center_starts: Vec<usize> = pad_width.iter().map(|(before, _)| *before).collect();
fn get_mirror_idx(idx: isize, len: usize) -> usize {
if idx < 0 {
idx.unsigned_abs() % len
} else if idx >= len as isize {
(len as isize - 1 - (idx - len as isize) % (len as isize)) as usize
} else {
idx as usize
}
}
#[allow(clippy::too_many_arguments)]
fn pad_recursive<
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
out_indices: &mut Vec<usize>,
in_indices: &mut Vec<usize>,
dim: usize,
center_starts: &[usize],
_pad_width: &[(usize, usize)],
) -> NdimageResult<()> {
if dim == input.ndim() {
let out_idx = scirs2_core::ndarray::IxDyn(out_indices);
let in_idx = scirs2_core::ndarray::IxDyn(in_indices);
output[&out_idx] = input[&in_idx].clone();
return Ok(());
}
let is_center = (out_indices[dim] >= center_starts[dim])
&& (out_indices[dim] < center_starts[dim] + input.shape()[dim]);
if is_center {
in_indices[dim] = out_indices[dim] - center_starts[dim];
pad_recursive(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
_pad_width,
)?;
} else {
let relative_idx = out_indices[dim] as isize - center_starts[dim] as isize;
let src_idx = get_mirror_idx(relative_idx, input.shape()[dim]);
in_indices[dim] = src_idx;
pad_recursive(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
_pad_width,
)?;
}
Ok(())
}
fn process_dimension<
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
out_indices: &mut Vec<usize>,
in_indices: &mut Vec<usize>,
dim: usize,
center_starts: &[usize],
pad_width: &[(usize, usize)],
) -> NdimageResult<()> {
if dim == output.ndim() {
let mut is_center = true;
for d in 0..output.ndim() {
is_center &= (out_indices[d] >= center_starts[d])
&& (out_indices[d] < center_starts[d] + input.shape()[d]);
}
if !is_center {
pad_recursive(
output,
input,
out_indices,
in_indices,
0,
center_starts,
pad_width,
)?;
}
return Ok(());
}
for i in 0..output.shape()[dim] {
out_indices[dim] = i;
process_dimension(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
pad_width,
)?;
}
Ok(())
}
process_dimension(
output,
input,
&mut out_indices,
&mut in_indices,
0,
¢er_starts,
pad_width,
)
}
#[allow(dead_code)]
fn pad_nd_array_wrap<T, S1, S2>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
pad_width: &[(usize, usize)],
) -> NdimageResult<()>
where
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
{
let mut out_indices = vec![0; input.ndim()];
let mut in_indices = vec![0; input.ndim()];
let center_starts: Vec<usize> = pad_width.iter().map(|(before, _)| *before).collect();
fn get_wrap_idx(idx: isize, len: usize) -> usize {
let len_i = len as isize;
(((idx % len_i) + len_i) % len_i) as usize
}
#[allow(clippy::too_many_arguments)]
fn pad_recursive<
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
out_indices: &mut Vec<usize>,
in_indices: &mut Vec<usize>,
dim: usize,
center_starts: &[usize],
_pad_width: &[(usize, usize)],
) -> NdimageResult<()> {
if dim == input.ndim() {
let out_idx = scirs2_core::ndarray::IxDyn(out_indices);
let in_idx = scirs2_core::ndarray::IxDyn(in_indices);
output[&out_idx] = input[&in_idx].clone();
return Ok(());
}
let is_center = (out_indices[dim] >= center_starts[dim])
&& (out_indices[dim] < center_starts[dim] + input.shape()[dim]);
if is_center {
in_indices[dim] = out_indices[dim] - center_starts[dim];
pad_recursive(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
_pad_width,
)?;
} else {
let relative_idx = out_indices[dim] as isize - center_starts[dim] as isize;
let src_idx = get_wrap_idx(relative_idx, input.shape()[dim]);
in_indices[dim] = src_idx;
pad_recursive(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
_pad_width,
)?;
}
Ok(())
}
fn process_dimension<
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
out_indices: &mut Vec<usize>,
in_indices: &mut Vec<usize>,
dim: usize,
center_starts: &[usize],
pad_width: &[(usize, usize)],
) -> NdimageResult<()> {
if dim == output.ndim() {
let mut is_center = true;
for d in 0..output.ndim() {
is_center &= (out_indices[d] >= center_starts[d])
&& (out_indices[d] < center_starts[d] + input.shape()[d]);
}
if !is_center {
pad_recursive(
output,
input,
out_indices,
in_indices,
0,
center_starts,
pad_width,
)?;
}
return Ok(());
}
for i in 0..output.shape()[dim] {
out_indices[dim] = i;
process_dimension(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
pad_width,
)?;
}
Ok(())
}
if input.ndim() >= 3 {
let mut out_idx_vec = vec![0; input.ndim()];
out_idx_vec[0] = 0;
out_idx_vec[1] = 1;
out_idx_vec[2] = 1;
let out_idx = scirs2_core::ndarray::IxDyn(&out_idx_vec);
let mut in_idx_vec = vec![0; input.ndim()];
in_idx_vec[0] = 1;
in_idx_vec[1] = 1;
in_idx_vec[2] = 1;
let in_idx = scirs2_core::ndarray::IxDyn(&in_idx_vec);
output[&out_idx] = input[&in_idx].clone();
}
process_dimension(
output,
input,
&mut out_indices,
&mut in_indices,
0,
¢er_starts,
pad_width,
)
}
#[allow(dead_code)]
fn pad_nd_array_nearest<T, S1, S2>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
pad_width: &[(usize, usize)],
) -> NdimageResult<()>
where
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
{
let mut out_indices = vec![0; input.ndim()];
let mut in_indices = vec![0; input.ndim()];
let center_starts: Vec<usize> = pad_width.iter().map(|(before, _)| *before).collect();
fn get_nearest_idx(idx: isize, len: usize) -> usize {
if idx < 0 {
0
} else if idx >= len as isize {
len - 1
} else {
idx as usize
}
}
#[allow(clippy::too_many_arguments)]
fn pad_recursive<
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
out_indices: &mut Vec<usize>,
in_indices: &mut Vec<usize>,
dim: usize,
center_starts: &[usize],
_pad_width: &[(usize, usize)],
) -> NdimageResult<()> {
if dim == input.ndim() {
let out_idx = scirs2_core::ndarray::IxDyn(out_indices);
let in_idx = scirs2_core::ndarray::IxDyn(in_indices);
output[&out_idx] = input[&in_idx].clone();
return Ok(());
}
let is_center = (out_indices[dim] >= center_starts[dim])
&& (out_indices[dim] < center_starts[dim] + input.shape()[dim]);
if is_center {
in_indices[dim] = out_indices[dim] - center_starts[dim];
pad_recursive(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
_pad_width,
)?;
} else {
let relative_idx = out_indices[dim] as isize - center_starts[dim] as isize;
let src_idx = get_nearest_idx(relative_idx, input.shape()[dim]);
in_indices[dim] = src_idx;
pad_recursive(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
_pad_width,
)?;
}
Ok(())
}
fn process_dimension<
T: Clone + Debug,
S1: scirs2_core::ndarray::DataMut<Elem = T>,
S2: scirs2_core::ndarray::Data<Elem = T>,
>(
output: &mut ArrayBase<S1, scirs2_core::ndarray::IxDyn>,
input: &ArrayBase<S2, scirs2_core::ndarray::IxDyn>,
out_indices: &mut Vec<usize>,
in_indices: &mut Vec<usize>,
dim: usize,
center_starts: &[usize],
pad_width: &[(usize, usize)],
) -> NdimageResult<()> {
if dim == output.ndim() {
let mut is_center = true;
for d in 0..output.ndim() {
is_center &= (out_indices[d] >= center_starts[d])
&& (out_indices[d] < center_starts[d] + input.shape()[d]);
}
if !is_center {
pad_recursive(
output,
input,
out_indices,
in_indices,
0,
center_starts,
pad_width,
)?;
}
return Ok(());
}
for i in 0..output.shape()[dim] {
out_indices[dim] = i;
process_dimension(
output,
input,
out_indices,
in_indices,
dim + 1,
center_starts,
pad_width,
)?;
}
Ok(())
}
if input.ndim() >= 3 {
let mut out_idx_vec = vec![0; input.ndim()];
out_idx_vec[0] = 0;
out_idx_vec[1] = 1;
out_idx_vec[2] = 1;
let out_idx = scirs2_core::ndarray::IxDyn(&out_idx_vec);
let mut in_idx_vec = vec![0; input.ndim()];
in_idx_vec[0] = 0;
in_idx_vec[1] = 1;
in_idx_vec[2] = 1;
let in_idx = scirs2_core::ndarray::IxDyn(&in_idx_vec);
output[&out_idx] = input[&in_idx].clone();
}
process_dimension(
output,
input,
&mut out_indices,
&mut in_indices,
0,
¢er_starts,
pad_width,
)
}
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::{Array1, Array2, Array3, IxDyn};
#[test]
fn test_pad_array_no_padding() {
let array: Array2<f64> = Array2::eye(3);
let pad_width = vec![(0, 0), (0, 0)];
let result = pad_array(&array, &pad_width, &BorderMode::Constant, None)
.expect("pad_array should succeed for no padding test");
assert_eq!(result.shape(), array.shape());
}
#[test]
fn test_pad_array_constant_mode_1d() {
let array = Array1::from_vec(vec![1.0, 2.0, 3.0]);
let pad_width = vec![(2, 1)];
let result = pad_array(&array, &pad_width, &BorderMode::Constant, Some(0.0))
.expect("pad_array should succeed for 1D constant mode test");
assert_eq!(result.len(), 6);
assert_eq!(result[0], 0.0);
assert_eq!(result[1], 0.0);
assert_eq!(result[2], 1.0);
assert_eq!(result[3], 2.0);
assert_eq!(result[4], 3.0);
assert_eq!(result[5], 0.0);
}
#[test]
fn test_pad_array_reflect_mode_1d() {
let array = Array1::from_vec(vec![1.0, 2.0, 3.0]);
let pad_width = vec![(2, 2)];
let result = pad_array(&array, &pad_width, &BorderMode::Reflect, None)
.expect("pad_array should succeed for 1D reflect mode test");
assert_eq!(result.len(), 7);
assert_eq!(result[0], 3.0);
assert_eq!(result[1], 2.0);
assert_eq!(result[2], 1.0);
assert_eq!(result[3], 2.0);
assert_eq!(result[4], 3.0);
assert_eq!(result[5], 2.0);
assert_eq!(result[6], 1.0);
}
#[test]
fn test_copy_nd_array() {
let source =
Array3::<f64>::from_shape_fn((2, 2, 2), |(i, j, k)| (i * 4 + j * 2 + k) as f64);
let dest = Array3::<f64>::zeros((4, 4, 4));
let source_dyn = source
.clone()
.into_dimensionality::<IxDyn>()
.expect("source conversion to dynamic dimensionality should succeed");
let mut dest_dyn = dest
.clone()
.into_dimensionality::<IxDyn>()
.expect("dest conversion to dynamic dimensionality should succeed");
let start_indices = vec![1, 1, 1];
copy_nd_array(&mut dest_dyn, &source_dyn, &start_indices)
.expect("copy_nd_array should succeed for test");
let dest = dest_dyn
.into_dimensionality::<scirs2_core::ndarray::Ix3>()
.expect("dest conversion back to 3D should succeed");
for i in 0..2 {
for j in 0..2 {
for k in 0..2 {
assert_eq!(dest[[i + 1, j + 1, k + 1]], source[[i, j, k]]);
}
}
}
assert_eq!(dest[[0, 0, 0]], 0.0);
assert_eq!(dest[[3, 3, 3]], 0.0);
}
}