imgal 0.3.1

A fast and open-source scientific image processing and algorithm library.
Documentation
use std::mem::MaybeUninit;

use ndarray::{Array, Array1, ArrayBase, ArrayViewMut, AsArray, Dimension, ViewRepr, Zip};

use crate::prelude::*;

/// Copy n-dimensional image data into an exisiting array.
///
/// # Description
///
/// Copies an input image into an exisiting array with the same shape and type.
///
/// # Arguments
///
/// * `data_a`: The input n-dimensional array to copy data from.
/// * `data_b`: The input n-dimensional array to copy data to.
/// * `threads`: The requested number of threads to use for parallel execution.
///   If `None` or `Some(1)` sequential execution is used. If `Some(0)`, then
///   the maximum available parallelism is used. Thread counts are clamped to
///   the systems maximum.
///
/// # Returns
///
/// * `Err(ImgalError)`: If `data_a.shape() != data_b.shape()`.
#[inline]
pub fn copy_into<'a, T, A, D>(
    data_a: A,
    mut data_b: ArrayViewMut<T, D>,
    threads: Option<usize>,
) -> Result<(), ImgalError>
where
    A: AsArray<'a, T, D>,
    D: Dimension,
    T: 'a + AsNumeric,
{
    let data_a: ArrayBase<ViewRepr<&'a T>, D> = data_a.into();
    if data_a.shape() != data_b.shape() {
        return Err(ImgalError::MismatchedArrayShapes {
            a_arr_name: "data_a",
            a_shape: data_a.shape().to_vec(),
            b_arr_name: "data_b",
            b_shape: data_b.shape().to_vec(),
        });
    }
    par!(threads,
        seq_exp: data_b.assign(&data_a),
        par_exp: Zip::from(data_a).and(data_b)
            .par_for_each(|&a, b| *b = a));
    Ok(())
}

/// Copy an n-dimensional image into a flat 1D array.
///
/// # Description
///
/// Copies an input n-dimensional image into a flat 1D array.
///
/// # Arguments
///
/// * `data`: The input n-dimensional image to flatten.
/// * `threads`: The requested number of threads to use for parallel execution.
///   If `None` or `Some(1)` sequential execution is used. If `Some(0)`, then
///   the maximum available parallelism is used. Thread counts are clamped to
///   the systems maximum.
///
/// # Returns
///
/// * `Array1<T>`: A flat 1D array of the input data.
#[inline]
pub fn copy_into_flat<'a, T, A, D>(data: A, threads: Option<usize>) -> Array1<T>
where
    A: AsArray<'a, T, D>,
    D: Dimension,
    T: 'a + AsNumeric,
{
    let data: ArrayBase<ViewRepr<&'a T>, D> = data.into();
    let dl = data.len();
    let shape = data.raw_dim();
    if let Some(s) = data.as_slice() {
        return Array1::from_vec(s.to_vec());
    }
    let seq_flat_cp = || {
        let mut arr: Vec<T> = Vec::with_capacity(dl);
        arr.extend(data.view().iter().copied());
        Array1::from_vec(arr)
    };
    let par_flat_cp = || {
        // SAFE: this is safe because we always write to all values in arr
        let mut arr: Vec<MaybeUninit<T>> = Vec::with_capacity(dl);
        unsafe { arr.set_len(dl) };
        let mut arr = Array1::from_vec(arr)
            .into_shape_with_order(shape)
            .expect("Failed to reshape flat array into input destination array shape.");
        Zip::from(data.view()).and(&mut arr).par_for_each(|&v, d| {
            d.write(v);
        });
        let arr = arr
            .into_shape_with_order(dl)
            .expect("Failed to reshape array into a flat 1D array.");
        unsafe { arr.assume_init() }
    };
    par!(threads,seq_exp: seq_flat_cp(), par_exp: par_flat_cp())
}

/// Duplicate an n-dimensional image.
///
/// # Description
///
/// Duplicates a input n-dimensional image by allocating a new array and copying
/// elements into it.
///
/// # Arguments
///
/// * `data`: The input n-dimensional image to duplicate.
/// * `threads`: The requested number of threads to use for parallel execution.
///   If `None` or `Some(1)` sequential execution is used. If `Some(0)`, then
///   the maximum available parallelism is used. Thread counts are clamped to
///   the systems maximum.
///
/// # Returns
///
/// * `Array<T, D>`: A duplicate of the input image.
#[inline]
pub fn duplicate<'a, T, A, D>(data: A, threads: Option<usize>) -> Array<T, D>
where
    A: AsArray<'a, T, D>,
    D: Dimension,
    T: 'a + AsNumeric,
{
    let data: ArrayBase<ViewRepr<&'a T>, D> = data.into();
    let dup_par = || {
        let mut dup: Array<T, D> = Array::from_elem(data.dim(), T::default());
        Zip::from(data.view())
            .and(dup.view_mut())
            .par_for_each(|&v, d| {
                *d = v;
            });
        dup
    };
    par!(threads,
        seq_exp: data.to_owned(),
        par_exp: dup_par())
}