diff --git a/arrayfire b/arrayfire index 31d247584..63807f442 160000 --- a/arrayfire +++ b/arrayfire @@ -1 +1 @@ -Subproject commit 31d247584d15d5bf352438c4a6c4ef11b5170590 +Subproject commit 63807f4421385af94dc77e3332f04a2a64a3b1ed diff --git a/src/algorithm/mod.rs b/src/algorithm/mod.rs index 68d480f63..62d3f983b 100644 --- a/src/algorithm/mod.rs +++ b/src/algorithm/mod.rs @@ -2,7 +2,7 @@ extern crate libc; use array::Array; use defines::AfError; -use self::libc::{c_int, c_uint}; +use self::libc::{c_int, c_uint, c_double}; type MutAfArray = *mut self::libc::c_longlong; type MutDouble = *mut self::libc::c_double; @@ -12,18 +12,18 @@ type AfArray = self::libc::c_longlong; #[allow(dead_code)] extern { fn af_sum(out: MutAfArray, input: AfArray, dim: c_int) -> c_int; - //fn af_sum_nan(out: MutAfArray, input: AfArray, dim: c_int, nanval: c_double) -> c_int; + fn af_sum_nan(out: MutAfArray, input: AfArray, dim: c_int, nanval: c_double) -> c_int; fn af_product(out: MutAfArray, input: AfArray, dim: c_int) -> c_int; - //fn af_product_nan(out: MutAfArray, input: AfArray, dim: c_int, val: c_double) -> c_int; + fn af_product_nan(out: MutAfArray, input: AfArray, dim: c_int, val: c_double) -> c_int; fn af_min(out: MutAfArray, input: AfArray, dim: c_int) -> c_int; fn af_max(out: MutAfArray, input: AfArray, dim: c_int) -> c_int; fn af_all_true(out: MutAfArray, input: AfArray, dim: c_int) -> c_int; fn af_any_true(out: MutAfArray, input: AfArray, dim: c_int) -> c_int; fn af_count(out: MutAfArray, input: AfArray, dim: c_int) -> c_int; fn af_sum_all(r: MutDouble, i: MutDouble, input: AfArray) -> c_int; - //fn af_sum_nan_all(r: MutDouble, i: MutDouble, input: AfArray, val: c_double) -> c_int; + fn af_sum_nan_all(r: MutDouble, i: MutDouble, input: AfArray, val: c_double) -> c_int; fn af_product_all(r: MutDouble, i: MutDouble, input: AfArray) -> c_int; - //fn af_product_nan_all(r: MutDouble, i: MutDouble, input: AfArray, val: c_double) -> c_int; + fn af_product_nan_all(r: MutDouble, i: MutDouble, input: AfArray, val: c_double) -> c_int; fn af_min_all(r: MutDouble, i: MutDouble, input: AfArray) -> c_int; fn af_max_all(r: MutDouble, i: MutDouble, input: AfArray) -> c_int; fn af_all_true_all(r: MutDouble, i: MutDouble, input: AfArray) -> c_int; @@ -85,23 +85,56 @@ dim_reduce_func_def!(accum, af_accum); dim_reduce_func_def!(diff1, af_diff1); dim_reduce_func_def!(diff2, af_diff2); -//pub fn sum_nan(input: &Array, dim: i32, nanval: f64) -> Array { -// unsafe { -// let mut temp: i64 = 0; -// af_sum_nan(&mut temp as MutAfArray, input.get() as AfArray, -// dim as c_int, nanval as c_double); -// Array {handle: temp} -// } -//} +/// Reduction operation along specific dimension +/// +/// Sum values of the `input` Array along `dim` dimension after replacing any `NAN` values in the +/// Array with `nanval` value. +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `dim` is reduction dimension +/// - `nanval` is value with which all the `NAN` values of Array are replaced with +/// +/// # Return Values +/// +/// Reduced Array +pub fn sum_nan(input: &Array, dim: i32, nanval: f64) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_sum_nan(&mut temp as MutAfArray, input.get() as AfArray, + dim as c_int, nanval as c_double); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} -//pub fn product_nan(input: &Array, dim: i32, nanval: f64) -> Array { -// unsafe { -// let mut temp: i64 = 0; -// af_product_nan(&mut temp as MutAfArray, input.get() as AfArray, -// dim as c_int, nanval as c_double); -// Array {handle: temp} -// } -//} +/// Reduction operation along specific dimension +/// +/// Compute product of the values of the `input` Array along `dim` dimension after replacing any `NAN` values in the Array with `nanval` value. +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `dim` is reduction dimension +/// - `nanval` is value with which all the `NAN` values of Array are replaced with +/// +/// # Return Values +/// +/// Reduced Array +pub fn product_nan(input: &Array, dim: i32, nanval: f64) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_product_nan(&mut temp as MutAfArray, input.get() as AfArray, + dim as c_int, nanval as c_double); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} macro_rules! all_reduce_func_def { ($fn_name: ident, $ffi_name: ident) => ( @@ -109,7 +142,7 @@ macro_rules! all_reduce_func_def { /// /// # Parameters /// - /// `input` - Input Array + /// - `input` is the input Array /// /// # Return Values /// @@ -139,25 +172,59 @@ all_reduce_func_def!(all_true_all, af_all_true_all); all_reduce_func_def!(any_true_all, af_any_true_all); all_reduce_func_def!(count_all, af_count_all); -//pub fn sum_nan_all(input: &Array, val: f64) -> (f64, f64) { -// unsafe { -// let mut real: f64 = 0.0; -// let mut imag: f64 = 0.0; -// af_sum_nan_all(&mut real as MutDouble, &mut imag as MutDouble, -// input.get() as AfArray, val as c_double); -// (real, imag) -// } -//} +/// Reduction operation of all values +/// +/// Sum all the values of the `input` Array after replacing any `NAN` values with `val`. +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `val` is the val that replaces all `NAN` values of the Array before reduction operation is +/// performed. +/// +/// # Return Values +/// +/// A tuple of reduction result. For non-complex data type Arrays, second value of tuple is +/// zero. +pub fn sum_nan_all(input: &Array, val: f64) -> Result<(f64, f64), AfError> { + unsafe { + let mut real: f64 = 0.0; + let mut imag: f64 = 0.0; + let err_val = af_sum_nan_all(&mut real as MutDouble, &mut imag as MutDouble, + input.get() as AfArray, val as c_double); + match err_val { + 0 => Ok((real, imag)), + _ => Err(AfError::from(err_val)), + } + } +} -//pub fn product_nan_all(input: &Array, val: f64) -> (f64, f64) { -// unsafe { -// let mut real: f64 = 0.0; -// let mut imag: f64 = 0.0; -// af_product_nan_all(&mut real as MutDouble, &mut imag as MutDouble, -// input.get() as AfArray, val as c_double); -// (real, imag) -// } -//} +/// Reduction operation of all values +/// +/// Compute the product of all the values of the `input` Array after replacing any `NAN` values with `val`. +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `val` is the val that replaces all `NAN` values of the Array before reduction operation is +/// performed. +/// +/// # Return Values +/// +/// A tuple of reduction result. For non-complex data type Arrays, second value of tuple is +/// zero. +pub fn product_nan_all(input: &Array, val: f64) -> Result<(f64, f64), AfError> { + unsafe { + let mut real: f64 = 0.0; + let mut imag: f64 = 0.0; + let err_val = af_product_nan_all(&mut real as MutDouble, &mut imag as MutDouble, + input.get() as AfArray, val as c_double); + match err_val { + 0 => Ok((real, imag)), + _ => Err(AfError::from(err_val)), + } + } +} macro_rules! dim_ireduce_func_def { ($fn_name: ident, $ffi_name: ident) => ( diff --git a/src/arith/mod.rs b/src/arith/mod.rs index 1ad374364..982597255 100644 --- a/src/arith/mod.rs +++ b/src/arith/mod.rs @@ -77,6 +77,7 @@ extern { fn af_atanh(out: MutAfArray, arr: AfArray) -> c_int; fn af_pow2(out: MutAfArray, arr: AfArray) -> c_int; fn af_exp(out: MutAfArray, arr: AfArray) -> c_int; + fn af_sigmoid(out: MutAfArray, arr: AfArray) -> c_int; fn af_expm1(out: MutAfArray, arr: AfArray) -> c_int; fn af_erf(out: MutAfArray, arr: AfArray) -> c_int; fn af_erfc(out: MutAfArray, arr: AfArray) -> c_int; @@ -150,6 +151,7 @@ unary_func!(acosh, af_acosh); unary_func!(atanh, af_atanh); unary_func!(pow2, af_pow2); unary_func!(exp, af_exp); +unary_func!(sigmoid, af_sigmoid); unary_func!(expm1, af_expm1); unary_func!(erf, af_erf); unary_func!(erfc, af_erfc); @@ -237,7 +239,7 @@ macro_rules! overloaded_binary_func { } } - pub fn $fn_name (arg1: &T, arg2: &U, batch: bool) -> Result { + pub fn $fn_name (arg1: &T, arg2: &U, batch: bool) -> Result where T: Convertable, U: Convertable { let lhs = arg1.convert(); let rhs = arg2.convert(); match (lhs.is_scalar().unwrap(), rhs.is_scalar().unwrap()) { diff --git a/src/array.rs b/src/array.rs index cee81e5cb..f54046be4 100644 --- a/src/array.rs +++ b/src/array.rs @@ -62,6 +62,8 @@ extern { fn af_release_array(arr: AfArray) -> c_int; fn af_print_array(arr: AfArray) -> c_int; + + fn af_cast(out: MutAfArray, arr: AfArray, aftype: uint8_t) -> c_int; } /// A multidimensional data container @@ -218,6 +220,18 @@ impl Array { is_func!(is_floating, af_is_floating); is_func!(is_integer, af_is_integer); is_func!(is_bool, af_is_bool); + + /// Cast the Array data type to `target_type` + pub fn cast(&self, target_type: Aftype) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_cast(&mut temp as MutAfArray, self.handle as AfArray, target_type as uint8_t); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } + } } /// Used for creating Array object from native resource id diff --git a/src/data/mod.rs b/src/data/mod.rs index 6401db0b0..e9d07a2bf 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -59,6 +59,13 @@ extern { fn af_flip(out: MutAfArray, arr: AfArray, dim: c_uint) -> c_int; fn af_lower(out: MutAfArray, arr: AfArray, is_unit_diag: c_int) -> c_int; fn af_upper(out: MutAfArray, arr: AfArray, is_unit_diag: c_int) -> c_int; + + fn af_select(out: MutAfArray, cond: AfArray, a: AfArray, b: AfArray) -> c_int; + fn af_select_scalar_l(out: MutAfArray, cond: AfArray, a: c_double, b: AfArray) -> c_int; + fn af_select_scalar_r(out: MutAfArray, cond: AfArray, a: AfArray, b: c_double) -> c_int; + + fn af_replace(a: AfArray, cond: AfArray, b: AfArray) -> c_int; + fn af_replace_scalar(a: AfArray, cond: AfArray, b: c_double) -> c_int; } pub trait ConstGenerator { @@ -176,6 +183,18 @@ cnst!(u8 , 7); /// Create an Array with constant value /// +/// The trait ConstGenerator has been defined internally for the following types: +/// +/// - i64 +/// - u64 +/// - num::Complex\ +/// - num::Complex\ +/// - f32 +/// - f64 +/// - i32 +/// - u32 +/// - u8 +/// /// # Parameters /// /// - `cnst` is the constant value to be filled in the Array @@ -543,3 +562,162 @@ pub fn upper(input: &Array, is_unit_diag: bool) -> Result { } } } + +/// Element wise conditional operator for Arrays +/// +/// This function does the C-equivalent of the following statement, except that the operation +/// happens on a GPU for all elements simultaneously. +/// +/// ``` +/// c = cond ? a : b; /// where cond, a & b are all objects of type Array +/// ``` +/// +/// # Parameters +/// +/// - `a` is the Array whose element will be assigned to output if corresponding element in `cond` Array is +/// `True` +/// - `cond` is the Array with conditional values +/// - `b` is the Array whose element will be assigned to output if corresponding element in `cond` Array is +/// `False` +/// +/// # Return Values +/// +/// An Array +#[allow(unused_mut)] +pub fn select(a: &Array, cond: &Array, b: &Array) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_select(&mut temp as MutAfArray, cond.get() as AfArray, + a.get() as AfArray, b.get() as AfArray); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Element wise conditional operator for Arrays +/// +/// This function does the C-equivalent of the following statement, except that the operation +/// happens on a GPU for all elements simultaneously. +/// +/// ``` +/// c = cond ? a : b; /// where a is a scalar(f64) and b is Array +/// ``` +/// +/// # Parameters +/// +/// - `a` is the scalar that is assigned to output if corresponding element in `cond` Array is +/// `True` +/// - `cond` is the Array with conditional values +/// - `b` is the Array whose element will be assigned to output if corresponding element in `cond` Array is +/// `False` +/// +/// # Return Values +/// +/// An Array +#[allow(unused_mut)] +pub fn selectl(a: f64, cond: &Array, b: &Array) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_select_scalar_l(&mut temp as MutAfArray, cond.get() as AfArray, + a as c_double, b.get() as AfArray); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Element wise conditional operator for Arrays +/// +/// This function does the C-equivalent of the following statement, except that the operation +/// happens on a GPU for all elements simultaneously. +/// +/// ``` +/// c = cond ? a : b; /// where a is Array and b is a scalar(f64) +/// ``` +/// +/// # Parameters +/// +/// - `a` is the Array whose element will be assigned to output if corresponding element in `cond` Array is +/// `True` +/// - `cond` is the Array with conditional values +/// - `b` is the scalar that is assigned to output if corresponding element in `cond` Array is +/// `False` +/// +/// # Return Values +/// +/// An Array +#[allow(unused_mut)] +pub fn selectr(a: &Array, cond: &Array, b: f64) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_select_scalar_r(&mut temp as MutAfArray, cond.get() as AfArray, + a.get() as AfArray, b as c_double); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Inplace replace in Array based on a condition +/// +/// This function does the C-equivalent of the following statement, except that the operation +/// happens on a GPU for all elements simultaneously. +/// +/// ``` +/// a = cond ? a : b; /// where cond, a & b are all objects of type Array +/// ``` +/// +/// # Parameters +/// +/// - `a` is the Array whose element will be replaced with element from `b` if corresponding element in `cond` Array is `True` +/// - `cond` is the Array with conditional values +/// - `b` is the Array whose element will replace the element in output if corresponding element in `cond` Array is +/// `False` +/// +/// # Return Values +/// +/// An Array +#[allow(unused_mut)] +pub fn replace(a: &mut Array, cond: &Array, b: &Array) -> Result<(), AfError> { + unsafe { + let err_val = af_replace(a.get() as AfArray, cond.get() as AfArray, b.get() as AfArray); + match err_val { + 0 => Ok(()), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Inplace replace in Array based on a condition +/// +/// This function does the C-equivalent of the following statement, except that the operation +/// happens on a GPU for all elements simultaneously. +/// +/// ``` +/// a = cond ? a : b; /// where cond, a are Arrays and b is scalar(f64) +/// ``` +/// +/// # Parameters +/// +/// - `a` is the Array whose element will be replaced with element from `b` if corresponding element in `cond` Array is `True` +/// - `cond` is the Array with conditional values +/// - `b` is the scalar that will replace the element in output if corresponding element in `cond` Array is +/// `False` +/// +/// # Return Values +/// +/// An Array +#[allow(unused_mut)] +pub fn replace_scalar(a: &mut Array, cond: &Array, b: f64) -> Result<(), AfError> { + unsafe { + let err_val = af_replace_scalar(a.get() as AfArray, cond.get() as AfArray, b as c_double); + match err_val { + 0 => Ok(()), + _ => Err(AfError::from(err_val)), + } + } +} diff --git a/src/defines.rs b/src/defines.rs index f881ca43e..9f6198ad2 100644 --- a/src/defines.rs +++ b/src/defines.rs @@ -254,3 +254,15 @@ pub enum ColorMap { /// Blue hue map BLUE = 6, } + +/// YCbCr Standards +#[repr(C)] +#[derive(Copy, Clone)] +pub enum YCCStd { + /// ITU-R BT.601 (formerly CCIR 601) standard + YCC_601 = 601, + /// ITU-R BT.709 standard + YCC_709 = 709, + /// ITU-R BT.2020 standard + YCC_2020 = 2020, +} diff --git a/src/device/mod.rs b/src/device/mod.rs index 234c73c24..631e2afcb 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -8,7 +8,15 @@ extern { fn af_info() -> c_int; + fn af_get_device_count(nDevices: *mut c_int) -> c_int; + + fn af_get_dbl_support(available: *mut c_int, device: c_int) -> c_int; + fn af_set_device(device: c_int) -> c_int; + + fn af_get_device(device: *mut c_int) -> c_int; + + fn af_sync(device: c_int) -> c_int; } /// Get ArrayFire Version Number @@ -30,6 +38,16 @@ pub fn get_version() -> Result<(i32, i32, i32), AfError> { } /// Print library meta-info +/// +/// # Examples +/// +/// An example output of `af::info` call looks like below +/// +/// ``` +/// ArrayFire v3.0.0 (CUDA, 64-bit Mac OSX, build d8d4b38) +/// Platform: CUDA Toolkit 7, Driver: CUDA Driver Version: 7000 +/// [0] GeForce GT 750M, 2048 MB, CUDA Compute 3.0 +/// ``` pub fn info() -> Result<(), AfError> { unsafe { let err_val = af_info(); @@ -40,6 +58,38 @@ pub fn info() -> Result<(), AfError> { } } +/// Get total number of available devices +pub fn device_count() -> Result { + unsafe { + let mut temp: i32 = 0; + let err_val = af_get_device_count(&mut temp as *mut c_int); + match err_val { + 0 => Ok(temp), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Check if a device has double support +/// +/// # Parameters +/// +/// - `device` is the device for which double support is checked for +/// +/// # Return Values +/// +/// `True` if `device` device has double support, `False` otherwise. +pub fn is_double_available(device: i32) -> Result { + unsafe { + let mut temp: i32 = 0; + let err_val = af_get_dbl_support(&mut temp as *mut c_int, device as c_int); + match err_val { + 0 => Ok(temp > 0), + _ => Err(AfError::from(err_val)), + } + } +} + /// Set active device /// /// # Parameters @@ -54,3 +104,34 @@ pub fn set_device(device: i32) -> Result<(), AfError> { } } } + +/// Get the current active device id +pub fn get_device() -> Result { + unsafe { + let mut temp: i32 = 0; + let err_val = af_get_device(&mut temp as *mut c_int); + match err_val { + 0 => Ok(temp), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Sync all operations on given device +/// +/// # Parameters +/// +/// - `device` on which the operations are to be synced +/// +/// # Return Values +/// +/// None +pub fn sync(device: i32) -> Result<(), AfError> { + unsafe { + let err_val = af_sync(device as c_int); + match err_val { + 0 => Ok(()), + _ => Err(AfError::from(err_val)), + } + } +} diff --git a/src/image/mod.rs b/src/image/mod.rs index 936fc3f8c..41008c17f 100644 --- a/src/image/mod.rs +++ b/src/image/mod.rs @@ -7,6 +7,7 @@ use defines::BorderType; use defines::ColorSpace; use defines::Connectivity; use defines::InterpType; +use defines::YCCStd; use self::libc::{uint8_t, c_uint, c_int, c_float, c_double}; type MutAfArray = *mut self::libc::c_longlong; @@ -72,6 +73,21 @@ extern { fn af_color_space(out: MutAfArray, input: AfArray, tospace: uint8_t, fromspace: uint8_t) -> c_int; + + fn af_unwrap(out: MutAfArray, input: AfArray, wx: DimT, wy: DimT, sx: DimT, sy: DimT, + px: DimT, py: DimT, is_column: c_int) -> c_int; + + fn af_wrap(out: MutAfArray, input: AfArray, + ox: DimT, oy: DimT, + wx: DimT, wy: DimT, + sx: DimT, sy: DimT, + px: DimT, py: DimT, + is_column: c_int) -> c_int; + + fn af_sat(out: MutAfArray, input: AfArray) -> c_int; + + fn af_ycbcr2rgb(out: MutAfArray, input: AfArray, stnd: c_int) -> c_int; + fn af_rgb2ycbcr(out: MutAfArray, input: AfArray, stnd: c_int) -> c_int; } /// Calculate the gradients @@ -267,30 +283,84 @@ pub fn rotate(input: &Array, theta: f64, crop: bool, } } -macro_rules! trans_func_def { - ($fn_name: ident, $ffi_name: ident) => ( - #[allow(unused_mut)] - pub fn $fn_name(input: &Array, p0: f32, p1: f32, - odim0: i64, odim1: i64, - method: InterpType) -> Result { - unsafe { - let mut temp: i64 = 0; - let err_val = $ffi_name(&mut temp as MutAfArray, - input.get() as AfArray, - p0 as c_float, p1 as c_float, - odim0 as DimT, odim1 as DimT, - method as uint8_t); - match err_val { - 0 => Ok(Array::from(temp)), - _ => Err(AfError::from(err_val)), - } - } +/// Translate an Image +/// +/// Translating an image is moving it along 1st and 2nd dimensions by trans0 and trans1. Positive +/// values of these will move the data towards negative x and negative y whereas negative values of +/// these will move the positive right and positive down. See the example below for more. +/// +/// To specify an output dimension, use the odim0 and odim1 for dim0 and dim1 respectively. The +/// size of 2rd and 3rd dimension is same as input. If odim0 and odim1 and not defined, then the +/// output dimensions are same as the input dimensions and the data out of bounds will be +/// discarded. +/// +/// All new values that do not map to a location of the input array are set to 0. +/// +/// Translate is a special case of the [transform](./fn.transform.html) function. +/// +/// # Parameters +/// +/// - `input` is input image +/// - `trans0` is amount by which the first dimension is translated +/// - `trans1` is amount by which the second dimension is translated +/// - `odim0` is the first output dimension +/// - `odim1` is the second output dimension +/// - `method` is the interpolation type (Nearest by default) +/// +/// # Return Values +/// +/// Translated Image(Array). +#[allow(unused_mut)] +pub fn translate(input: &Array, trans0: f32, trans1: f32, + odim0: i64, odim1: i64, method: InterpType) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_translate(&mut temp as MutAfArray, + input.get() as AfArray, + trans0 as c_float, trans1 as c_float, + odim0 as DimT, odim1 as DimT, + method as uint8_t); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), } - ) + } } -trans_func_def!(translate, af_translate); -trans_func_def!(scale, af_scale); +/// Scale an Image +/// +/// Scale is the same functionality as [resize](./fn.resize.html) except that the scale function uses the transform kernels. The other difference is that scale does not set boundary values to be the boundary of the input array. Instead these are set to 0. +/// +/// Scale is a special case of the [transform](./fn.transform.html) function. +/// +/// # Parameters +/// +/// - `input` is input image +/// - `trans0` is amount by which the first dimension is translated +/// - `trans1` is amount by which the second dimension is translated +/// - `odim0` is the first output dimension +/// - `odim1` is the second output dimension +/// - `method` is the interpolation type (Nearest by default) +/// +/// # Return Values +/// +/// Translated Image(Array). +#[allow(unused_mut)] +pub fn scale(input: &Array, scale0: f32, scale1: f32, + odim0: i64, odim1: i64, method: InterpType) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_scale(&mut temp as MutAfArray, + input.get() as AfArray, + scale0 as c_float, scale1 as c_float, + odim0 as DimT, odim1 as DimT, + method as uint8_t); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} /// Skew an image /// @@ -369,27 +439,122 @@ pub fn histogram(input: &Array, nbins: u32, } } -macro_rules! morph_func_def { - ($fn_name: ident, $ffi_name: ident) => ( - #[allow(unused_mut)] - pub fn $fn_name(input: &Array, mask: &Array) -> Result { - unsafe { - let mut temp: i64 = 0; - let err_val = $ffi_name(&mut temp as MutAfArray, - input.get() as AfArray, mask.get() as AfArray); - match err_val { - 0 => Ok(Array::from(temp)), - _ => Err(AfError::from(err_val)), - } - } +/// Dilate an Image +/// +/// The dilation function takes two pieces of data as inputs. The first is the input image to be +/// morphed, and the second is the mask indicating the neighborhood around each pixel to match. +/// +/// In dilation, for each pixel, the mask is centered at the pixel. If the center pixel of the mask +/// matches the corresponding pixel on the image, then the mask is accepted. If the center pixels +/// do not matches, then the mask is ignored and no changes are made. +/// +/// For further reference, see [here](https://en.wikipedia.org/wiki/Dilation_(morphology)). +/// +/// # Parameters +/// +/// - `input` is the input image +/// - `mask` is the morphological operation mask +/// +/// # Return Values +/// +/// Dilated Image(Array) +#[allow(unused_mut)] +pub fn dilate(input: &Array, mask: &Array) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_dilate(&mut temp as MutAfArray, + input.get() as AfArray, mask.get() as AfArray); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), } - ) + } } -morph_func_def!(dilate, af_dilate); -morph_func_def!(erode, af_erode); -morph_func_def!(dilate3, af_dilate3); -morph_func_def!(erode3, af_erode3); +/// Erode an Image +/// +/// The erosion function is a morphological transformation on an image that requires two inputs. +/// The first is the image to be morphed, and the second is the mask indicating neighborhood that +/// must be white in order to preserve each pixel. +/// +/// In erode, for each pixel, the mask is centered at the pixel. If each pixel of the mask matches +/// the corresponding pixel on the image, then no change is made. If there is at least one +/// mismatch, then pixels are changed to the background color (black). +/// +/// For further reference, see [here](https://en.wikipedia.org/wiki/Erosion_(morphology)). +/// +/// # Parameters +/// +/// - `input` is the input image +/// - `mask` is the morphological operation mask +/// +/// # Return Values +/// +/// Eroded Image(Array) +#[allow(unused_mut)] +pub fn erode(input: &Array, mask: &Array) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_erode(&mut temp as MutAfArray, + input.get() as AfArray, mask.get() as AfArray); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Dilate a Volume +/// +/// Dilation for a volume is similar to the way dilation works on an image. Only difference is that +/// the masking operation is performed on a volume instead of a rectangular region. +/// +/// # Parameters +/// +/// - `input` is the input volume +/// - `mask` is the morphological operation mask +/// +/// # Return Values +/// +/// Dilated Volume(Array) +#[allow(unused_mut)] +pub fn dilate3(input: &Array, mask: &Array) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_dilate3(&mut temp as MutAfArray, + input.get() as AfArray, mask.get() as AfArray); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Erode a Volume +/// +/// Erosion for a volume is similar to the way erosion works on an image. Only difference is that +/// the masking operation is performed on a volume instead of a rectangular region. +/// +/// # Parameters +/// +/// - `input` is the input volume +/// - `mask` is the morphological operation mask +/// +/// # Return Values +/// +/// Eroded Volume(Array) +#[allow(unused_mut)] +pub fn erode3(input: &Array, mask: &Array) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_erode3(&mut temp as MutAfArray, + input.get() as AfArray, mask.get() as AfArray); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} /// Bilateral Filter. /// @@ -526,6 +691,8 @@ pub fn gaussian_kernel(rows: i32, cols: i32, /// - GRAY => RGB /// - RGB => HSV /// - HSV => RGB +/// - YCbCr => RGB +/// - RGB => YCbCr /// /// RGB (Red, Green, Blue) is the most common format used in computer imaging. RGB stores /// individual values for red, green and blue, and hence the 3 values per pixel. A combination of @@ -694,3 +861,217 @@ macro_rules! hsvrgb_func_def { hsvrgb_func_def!(hsv2rgb, af_hsv2rgb); hsvrgb_func_def!(rgb2hsv, af_rgb2hsv); + +/// Generate an array with image windows as columns +/// +/// unwrap takes in an input image along with the window sizes wx and wy, strides sx and sy, and +/// padding px and py. This function then generates a matrix where each windows is an independent +/// column. +/// +/// The number of columns (rows if is_column is true) in the output array are govenered by the +/// number of windows that can be fit along x and y directions. Padding is applied along all 4 +/// sides of the matrix with px defining the height of the padding along dim 0 and py defining the +/// width of the padding along dim 1. +/// +/// The first column window is always at the top left corner of the input including padding. If a +/// window cannot fit before the end of the matrix + padding, it is skipped from the generated +/// matrix. +/// +/// Padding can take a maximum value of window - 1 repectively for x and y. +/// +/// For multiple channels (3rd and 4th dimension), the generated matrix contains the same number of +/// channels as the input matrix. Each channel of the output matrix corresponds to the same channel +/// of the input. +/// +/// # Parameters +/// +/// - `input` is the input image +/// - `wx` is the block window size along 0th-dimension between [1, input.dims[0] + px] +/// - `wy` is the block window size along 1st-dimension between [1, input.dims[1] + py] +/// - `sx` is the stride along 0th-dimension +/// - `sy` is the stride along 1st-dimension +/// - `px` is the padding along 0th-dimension between [0, wx). Padding is applied both before and after. +/// - `py` is the padding along 1st-dimension between [0, wy). Padding is applied both before and after. +/// - `is_column` specifies the layout for the unwrapped patch. If is_column is false, the unrapped patch is laid out as a row. +/// +/// # Return Values +/// +/// An Array with image windows as columns +/// +/// # Examples +/// +/// ``` +/// A [5 5 1 1] +/// 10 15 20 25 30 +/// 11 16 21 26 31 +/// 12 17 22 27 32 +/// 13 18 23 28 33 +/// 14 19 24 29 34 +/// +/// // Window 3x3, strides 1x1, padding 0x0 +/// unwrap(A, 3, 3, 1, 1, 0, 0, False) [9 9 1 1] +/// 10 11 12 15 16 17 20 21 22 +/// 11 12 13 16 17 18 21 22 23 +/// 12 13 14 17 18 19 22 23 24 +/// 15 16 17 20 21 22 25 26 27 +/// 16 17 18 21 22 23 26 27 28 +/// 17 18 19 22 23 24 27 28 29 +/// 20 21 22 25 26 27 30 31 32 +/// 21 22 23 26 27 28 31 32 33 +/// 22 23 24 27 28 29 32 33 34 +/// +/// // Window 3x3, strides 1x1, padding 1x1 +/// unwrap(A, 3, 3, 1, 1, 1, 1, False) [9 25 1 1] +/// 0 0 0 0 0 0 10 11 12 13 0 15 16 17 18 0 20 21 22 23 0 25 26 27 28 +/// 0 0 0 0 0 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 +/// 0 0 0 0 0 11 12 13 14 0 16 17 18 19 0 21 22 23 24 0 26 27 28 29 0 +/// 0 10 11 12 13 0 15 16 17 18 0 20 21 22 23 0 25 26 27 28 0 30 31 32 33 +/// 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 +/// 11 12 13 14 0 16 17 18 19 0 21 22 23 24 0 26 27 28 29 0 31 32 33 34 0 +/// 0 15 16 17 18 0 20 21 22 23 0 25 26 27 28 0 30 31 32 33 0 0 0 0 0 +/// 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 0 0 0 0 0 +/// 16 17 18 19 0 21 22 23 24 0 26 27 28 29 0 31 32 33 34 0 0 0 0 0 0 +/// ``` +pub fn unwrap(input: &Array, + wx: i64, wy: i64, + sx: i64, sy: i64, + px: i64, py: i64, + is_column: bool) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_unwrap(&mut temp as MutAfArray, input.get() as AfArray, + wx, wy, sx, sy, px, py, is_column as c_int); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Converts unwrapped image to an image +/// +/// Wrap takes an unwrapped image (see unwrap()) and converts it back to an image. +/// +/// The inputs to this function should be the same as the inputs used to generate the unwrapped +/// image. +/// +/// # Parameters +/// +/// - `input` is the output of unwrap function call +/// - `ox` is the 0th-dimension of output image +/// - `oy` is the 1st-dimension of output image +/// - `wx` is the block window size along 0th-dimension between +/// - `wy` is the block window size along 1st-dimension between +/// - `sx` is the stride along 0th-dimension +/// - `sy` is the stride along 1st-dimension +/// - `px` is the padding used along 0th-dimension between [0, wx). +/// - `py` is the padding used along 1st-dimension between [0, wy). +/// - `is_column` specifies the layout for the unwrapped patch. If is_column is false, the rows are treated as the patches +/// +/// # Return Values +/// +/// Image(Array) created from unwrapped Image(Array) +pub fn wrap(input: &Array, + ox: i64, oy: i64, wx: i64, wy: i64, + sx: i64, sy: i64, px: i64, py: i64, + is_column: bool) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_wrap(&mut temp as MutAfArray, input.get() as AfArray, + ox, oy, wx, wy, sx, sy, px, py, is_column as c_int); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Summed area table of an Image +/// +/// # Parameters +/// +/// - `input` is the input image +/// +/// # Return Values +/// +/// Summed area table (a.k.a Integral Image) of the input image. +pub fn sat(input: &Array) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_sat(&mut temp as MutAfArray, input.get() as AfArray); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// RGB to YCbCr colorspace converter. +/// +/// RGB (Red, Green, Blue) is the most common format used in computer imaging. RGB stores +/// individual values for red, green and blue, and hence the 3 values per pixel. A combination of +/// these three values produces the gamut of unique colors. +/// +/// YCbCr is a family of color spaces used as a part of the color image pipeline in video and +/// digital photography systems where Y is luma component and Cb & Cr are the blue-difference and +/// red-difference chroma components. +/// +/// Input array to this function should be of real data in the range [0,1]. +/// +/// # Parameters +/// +/// - `input` is the input image in RGB color space +/// - `standard` is the target color space - [YCbCr standard](./enum.YCCStd.html) +/// +/// # Return Values +/// +/// Image(Array) in YCbCr color space +pub fn rgb2ycbcr(input: &Array, standard: YCCStd) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_rgb2ycbcr(&mut temp as MutAfArray, input.get() as AfArray, + standard as c_int); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// YCbCr to RGB colorspace converter. +/// +/// YCbCr is a family of color spaces used as a part of the color image pipeline in video and +/// digital photography systems where Y is luma component and Cb & Cr are the blue-difference and +/// red-difference chroma components. +/// +/// RGB (Red, Green, Blue) is the most common format used in computer imaging. RGB stores +/// individual values for red, green and blue, and hence the 3 values per pixel. A combination of +/// these three values produces the gamut of unique colors. +/// +/// Input array to this function should be of real data with the following range in their +/// respective channels. +/// +/// - Y −> [16,219] +/// - Cb −> [16,240] +/// - Cr −> [16,240] +/// +/// # Parameters +/// +/// - `input` is the input image in YCbCr color space +/// - `standard` is the [YCbCr standard](./enum.YCCStd.html) in which input image color space is +/// present. +/// +/// # Return Values +/// +/// Image(Array) in RGB color space +pub fn ycbcr2rgb(input: &Array, standard: YCCStd) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_ycbcr2rgb(&mut temp as MutAfArray, input.get() as AfArray, + standard as c_int); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} diff --git a/src/lapack/mod.rs b/src/lapack/mod.rs index a05e7968c..ca02805db 100644 --- a/src/lapack/mod.rs +++ b/src/lapack/mod.rs @@ -12,6 +12,8 @@ type AfArray = self::libc::c_longlong; #[allow(dead_code)] extern { + fn af_svd(u: MutAfArray, s: MutAfArray, vt: MutAfArray, input: AfArray) -> c_int; + fn af_svd_inplace(u: MutAfArray, s: MutAfArray, vt: MutAfArray, input: AfArray) -> c_int; fn af_lu(lower: MutAfArray, upper: MutAfArray, pivot: MutAfArray, input: AfArray) -> c_int; fn af_lu_inplace(pivot: MutAfArray, input: AfArray, is_lapack_piv: c_int) -> c_int; fn af_qr(q: MutAfArray, r: MutAfArray, tau: MutAfArray, input: AfArray) -> c_int; @@ -26,6 +28,83 @@ extern { fn af_norm(out: MutDouble, input: AfArray, ntype: uint8_t, p: c_double, q: c_double) -> c_int; } +/// Perform Singular Value Decomposition +/// +/// This function factorizes a matrix A into two unitary matrices U and Vt, and a diagonal matrix S +/// such that +/// +/// A = U∗S∗Vt +/// +/// If A has M rows and N columns, U is of the size M x M , V is of size N x N, and S is of size M +/// x N +/// +/// # Parameters +/// +/// - `in` is the input matrix +/// +/// # Return Values +/// +/// A triplet of Arrays. +/// +/// The first Array is the output array containing U +/// +/// The second Array is the output array containing the diagonal values of sigma, (singular values of the input matrix)) +/// +/// The third Array is the output array containing V ^ H +#[allow(unused_mut)] +pub fn svd(input: &Array) -> Result<(Array, Array, Array), AfError> { + unsafe { + let mut u: i64 = 0; + let mut s: i64 = 0; + let mut vt: i64 = 0; + let err_val = af_svd(&mut u as MutAfArray, &mut s as MutAfArray, &mut vt as MutAfArray, + input.get() as AfArray); + match err_val { + 0 => Ok((Array::from(u), Array::from(s), Array::from(vt))), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Perform Singular Value Decomposition inplace +/// +/// This function factorizes a matrix A into two unitary matrices U and Vt, and a diagonal matrix S +/// such that +/// +/// A = U∗S∗Vt +/// +/// If A has M rows and N columns, U is of the size M x M , V is of size N x N, and S is of size M +/// x N +/// +/// # Parameters +/// +/// - `in` is the input/output matrix. This will contain random data after the function call is +/// complete. +/// +/// # Return Values +/// +/// A triplet of Arrays. +/// +/// The first Array is the output array containing U +/// +/// The second Array is the output array containing the diagonal values of sigma, (singular values of the input matrix)) +/// +/// The third Array is the output array containing V ^ H +#[allow(unused_mut)] +pub fn svd_inplace(input: &mut Array) -> Result<(Array, Array, Array), AfError> { + unsafe { + let mut u: i64 = 0; + let mut s: i64 = 0; + let mut vt: i64 = 0; + let err_val = af_svd_inplace(&mut u as MutAfArray, &mut s as MutAfArray, + &mut vt as MutAfArray, input.get() as AfArray); + match err_val { + 0 => Ok((Array::from(u), Array::from(s), Array::from(vt))), + _ => Err(AfError::from(err_val)), + } + } +} + /// Perform LU decomposition /// /// # Parameters @@ -60,7 +139,7 @@ pub fn lu(input: &Array) -> Result<(Array, Array, Array), AfError> { /// /// # Parameters /// -/// - `input` is the input matrix +/// - `input` contains the input matrix on entry and packed LU decomposition on exit /// - `is_lapack_pic` specified if the pivot is returned in original LAPACK compliant format /// /// # Return Values @@ -115,7 +194,7 @@ pub fn qr(input: &Array) -> Result<(Array, Array, Array), AfError> { /// /// # Parameters /// -/// - `input` is the input matrix +/// - `input` contains the input matrix on entry, and packed QR decomposition on exit /// /// # Return Values /// @@ -165,7 +244,7 @@ pub fn cholesky(input: &Array, is_upper: bool) -> Result<(Array, i32), AfError> /// /// # Parameters /// -/// - `input` is the input matrix +/// - `input` contains the input matrix on entry, and triangular matrix on exit. /// - `is_upper` is a boolean to indicate if the output has to be upper or lower triangular matrix /// /// # Return Values diff --git a/src/lib.rs b/src/lib.rs index b665bd527..aef7e2c32 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,8 +7,8 @@ pub use array::{print}; mod array; //pub use algorithm::{sum_nan, product_nan, sum_nan_all, product_nan_all}; -pub use algorithm::{sum, product, min, max, all_true, any_true, count}; -pub use algorithm::{sum_all, product_all, min_all, max_all}; +pub use algorithm::{sum, product, min, max, all_true, any_true, count, sum_nan, product_nan}; +pub use algorithm::{sum_all, product_all, min_all, max_all, sum_nan_all, product_nan_all}; pub use algorithm::{all_true_all, any_true_all, count_all, imin, imax, imin_all, imax_all}; pub use algorithm::{accum, locate, diff1, diff2, sort, sort_index, sort_by_key}; pub use algorithm::{set_unique, set_union, set_intersect}; @@ -16,7 +16,7 @@ mod algorithm; pub use arith::{add, sub, div, mul, lt, gt, le, ge, eq, neq, and, or, minof, maxof, rem}; pub use arith::{bitand, bitor, bitxor, shiftl, shiftr}; -pub use arith::{abs, sign, round, trunc, floor, ceil, modulo}; +pub use arith::{abs, sign, round, trunc, floor, ceil, modulo, sigmoid}; pub use arith::{sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh}; pub use arith::{atan2, cplx2, arg, cplx, real, imag, conjg, hypot}; pub use arith::{sqrt, log, log1p, log10, log2, pow2, exp, expm1, erf, erfc, root, pow}; @@ -31,12 +31,13 @@ pub use data::{set_seed, get_seed, randu, randn}; pub use data::{identity, diag_create, diag_extract, lower, upper}; pub use data::{join, join_many, tile}; pub use data::{reorder, shift, moddims, flat, flip}; +pub use data::{select, selectl, selectr, replace, replace_scalar}; mod data; -pub use device::{get_version, info, set_device}; +pub use device::{get_version, info, device_count, is_double_available, set_device, get_device, sync}; mod device; -pub use defines::{Aftype, AfError, ColorMap}; +pub use defines::{Aftype, AfError, ColorMap, YCCStd}; pub use defines::{InterpType, BorderType, MatchType, NormType}; pub use defines::{Connectivity, ConvMode, ConvDomain, ColorSpace, MatProp}; mod defines; @@ -61,13 +62,18 @@ pub use image::{dilate, dilate3, erode, erode3, minfilt, maxfilt}; pub use image::{gradient, histogram, hist_equal, regions}; pub use image::{gray2rgb, rgb2gray, hsv2rgb, rgb2hsv, color_space}; pub use image::{bilateral, mean_shift, medfilt, sobel}; +pub use image::{unwrap, wrap, sat, rgb2ycbcr, ycbcr2rgb}; mod image; -pub use lapack::{lu, lu_inplace, qr, qr_inplace, cholesky, cholesky_inplace, solve, solve_lu, inverse, det, rank, norm}; +pub use lapack::{svd, lu, qr, cholesky, solve, solve_lu, inverse, det, rank, norm}; +pub use lapack::{svd_inplace, lu_inplace, qr_inplace, cholesky_inplace}; mod lapack; pub use signal::{approx1, approx2}; pub use signal::{fft, fft2, fft3, ifft, ifft2, ifft3}; +pub use signal::{fft_r2c, fft2_r2c, fft3_r2c, fft_c2r, fft2_c2r, fft3_c2r}; +pub use signal::{fft_inplace, fft2_inplace, fft3_inplace}; +pub use signal::{ifft_inplace, ifft2_inplace, ifft3_inplace}; pub use signal::{convolve1, convolve2, convolve3, convolve2_sep}; pub use signal::{fft_convolve1, fft_convolve2, fft_convolve3}; pub use signal::{fir, iir}; @@ -82,5 +88,5 @@ mod statistics; mod util; pub use vision::Features; -pub use vision::{fast, orb, hamming_matcher, match_template}; +pub use vision::{fast, harris, orb, hamming_matcher, nearest_neighbour, match_template, susan, dog}; mod vision; diff --git a/src/signal/mod.rs b/src/signal/mod.rs index 78fcb18e5..10e566a81 100644 --- a/src/signal/mod.rs +++ b/src/signal/mod.rs @@ -36,6 +36,24 @@ extern { fn af_ifft3(out: MutAfArray, arr: AfArray, nfac: c_double, odim0: c_longlong, odim1: c_longlong, odim2: c_longlong) -> c_int; + fn af_fft_inplace(arr: AfArray, nfac: c_double) -> c_int; + fn af_fft2_inplace(arr: AfArray, nfac: c_double) -> c_int; + fn af_fft3_inplace(arr: AfArray, nfac: c_double) -> c_int; + fn af_ifft_inplace(arr: AfArray, nfac: c_double) -> c_int; + fn af_ifft2_inplace(arr: AfArray, nfac: c_double) -> c_int; + fn af_ifft3_inplace(arr: AfArray, nfac: c_double) -> c_int; + + fn af_fft_r2c(out: MutAfArray, arr: AfArray, + nfac: c_double, pad0: c_longlong) -> c_int; + fn af_fft2_r2c(out: MutAfArray, arr: AfArray, + nfac: c_double, pad0: c_longlong, pad1: c_longlong) -> c_int; + fn af_fft3_r2c(out: MutAfArray, arr: AfArray, + nfac: c_double, pad0: c_longlong, pad1: c_longlong, pad2: c_longlong) -> c_int; + + fn af_fft_c2r(out: MutAfArray, input: AfArray, nfac: c_double, is_odd: c_int) -> c_int; + fn af_fft2_c2r(out: MutAfArray, input: AfArray, nfac: c_double, is_odd: c_int) -> c_int; + fn af_fft3_c2r(out: MutAfArray, input: AfArray, nfac: c_double, is_odd: c_int) -> c_int; + fn af_convolve1(out: MutAfArray, s: AfArray, f: AfArray, m: uint8_t, d: uint8_t) -> c_int; fn af_convolve2(out: MutAfArray, s: AfArray, f: AfArray, m: uint8_t, d: uint8_t) -> c_int; fn af_convolve3(out: MutAfArray, s: AfArray, f: AfArray, m: uint8_t, d: uint8_t) -> c_int; @@ -426,3 +444,241 @@ pub fn iir(b: &Array, a: &Array, x: &Array) -> Result { } } } + +/// In place 1d dimensional Fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor +pub fn fft_inplace(input: &Array, norm_factor: f64) -> Result<(), AfError> { + unsafe { + let err_val = af_fft_inplace(input.get() as AfArray, norm_factor as c_double); + match err_val { + 0 => Ok(()), + _ => Err(AfError::from(err_val)), + } + } +} + +/// In place 2d dimensional Fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor +pub fn fft2_inplace(input: &Array, norm_factor: f64) -> Result<(), AfError> { + unsafe { + let err_val = af_fft2_inplace(input.get() as AfArray, norm_factor as c_double); + match err_val { + 0 => Ok(()), + _ => Err(AfError::from(err_val)), + } + } +} + +/// In place 3d dimensional Fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor +pub fn fft3_inplace(input: &Array, norm_factor: f64) -> Result<(), AfError> { + unsafe { + let err_val = af_fft3_inplace(input.get() as AfArray, norm_factor as c_double); + match err_val { + 0 => Ok(()), + _ => Err(AfError::from(err_val)), + } + } +} + +/// In place 1d dimensional inverse fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor +pub fn ifft_inplace(input: &Array, norm_factor: f64) -> Result<(), AfError> { + unsafe { + let err_val = af_ifft_inplace(input.get() as AfArray, norm_factor as c_double); + match err_val { + 0 => Ok(()), + _ => Err(AfError::from(err_val)), + } + } +} + +/// In place 2d dimensional inverse fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor +pub fn ifft2_inplace(input: &Array, norm_factor: f64) -> Result<(), AfError> { + unsafe { + let err_val = af_ifft2_inplace(input.get() as AfArray, norm_factor as c_double); + match err_val { + 0 => Ok(()), + _ => Err(AfError::from(err_val)), + } + } +} + +/// In place 3d dimensional inverse fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor +pub fn ifft3_inplace(input: &Array, norm_factor: f64) -> Result<(), AfError> { + unsafe { + let err_val = af_ifft3_inplace(input.get() as AfArray, norm_factor as c_double); + match err_val { + 0 => Ok(()), + _ => Err(AfError::from(err_val)), + } + } +} + +/// 1d Real to Complex fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor to be applied before fft is applied +/// - `pad0` is the padding along 0th dimension of Array +/// +/// # Return Values +/// +/// Complex Array +pub fn fft_r2c(input: &Array, norm_factor: f64, pad0: i64) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_fft_r2c(&mut temp as MutAfArray, input.get() as AfArray, + norm_factor as c_double, pad0 as c_longlong); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// 2d Real to Complex fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor to be applied before fft is applied +/// - `pad0` is the padding along 0th dimension of Array +/// - `pad1` is the padding along 1st dimension of Array +/// +/// # Return Values +/// +/// Complex Array +pub fn fft2_r2c(input: &Array, norm_factor: f64, pad0: i64, pad1: i64) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_fft2_r2c(&mut temp as MutAfArray, input.get() as AfArray, + norm_factor as c_double, pad0 as c_longlong, pad1 as c_longlong); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// 3d Real to Complex fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor to be applied before fft is applied +/// - `pad0` is the padding along 0th dimension of Array +/// - `pad1` is the padding along 1st dimension of Array +/// - `pad2` is the padding along 2nd dimension of Array +/// +/// # Return Values +/// +/// Complex Array +pub fn fft3_r2c(input: &Array, norm_factor: f64, pad0: i64, pad1: i64, pad2: i64) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_fft3_r2c(&mut temp as MutAfArray, input.get() as AfArray, + norm_factor as c_double, pad0 as c_longlong, + pad1 as c_longlong, pad2 as c_longlong); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// 1d Complex to Real fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor to be applied before fft is applied +/// - `is_odd` signifies if the output should be even or odd size +/// +/// # Return Values +/// +/// Complex Array +pub fn fft_c2r(input: &Array, norm_factor: f64, is_odd: bool) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_fft_c2r(&mut temp as MutAfArray, input.get() as AfArray, + norm_factor as c_double, is_odd as c_int); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// 2d Complex to Real fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor to be applied before fft is applied +/// - `is_odd` signifies if the output should be even or odd size +/// +/// # Return Values +/// +/// Complex Array +pub fn fft2_c2r(input: &Array, norm_factor: f64, is_odd: bool) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_fft2_c2r(&mut temp as MutAfArray, input.get() as AfArray, + norm_factor as c_double, is_odd as c_int); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} + +/// 3d Complex to Real fast fourier transform +/// +/// # Parameters +/// +/// - `input` is the input Array +/// - `norm_factor` is the normalization factor to be applied before fft is applied +/// - `is_odd` signifies if the output should be even or odd size +/// +/// # Return Values +/// +/// Complex Array +pub fn fft3_c2r(input: &Array, norm_factor: f64, is_odd: bool) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_fft3_c2r(&mut temp as MutAfArray, input.get() as AfArray, + norm_factor as c_double, is_odd as c_int); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +} diff --git a/src/vision/mod.rs b/src/vision/mod.rs index a27f9db8b..ba51e43af 100644 --- a/src/vision/mod.rs +++ b/src/vision/mod.rs @@ -26,6 +26,8 @@ extern { fn af_fast(out: MutFeat, input: AfArray, thr: c_float, arc_len: c_uint, non_max: c_int, feature_ratio: c_float, edge: c_uint) -> c_int; + fn af_harris(out: MutFeat, input: AfArray, m: c_uint, r: c_float, s: c_float, bs: c_uint, k: c_float) -> c_int; + fn af_orb(out: MutFeat, desc: MutAfArray, arr: AfArray, fast_thr: c_float, max_feat: c_uint, scl_fctr: c_float, levels: c_uint, blur_img: c_int) -> c_int; @@ -33,8 +35,15 @@ extern { query: AfArray, train: AfArray, dist_dim: DimT, n_dist: c_uint) -> c_int; + fn af_nearest_neighbour(idx: MutAfArray, dist: MutAfArray, q: AfArray, t: AfArray, + dist_dim: DimT, n_dist: c_uint, dist_type: c_int) -> c_int; + fn af_match_template(out: MutAfArray, search_img: AfArray, template_img: AfArray, mtype: uint8_t) -> c_int; + + fn af_susan(feat: MutFeat, i: AfArray, r: c_uint, d: c_float, g: c_float, f: c_float, e: c_uint) -> c_int; + + fn af_dog(out: MutAfArray, i: AfArray, r1: c_int, r2: c_int) -> c_int; } /// A set of Array objects (usually, used in Computer vision context) @@ -174,6 +183,42 @@ pub fn fast(input: &Array, thr: f32, arc_len: u32, } } +/// Harris corner detector. +/// +/// Compute corners using the Harris corner detector approach. For each pixel, a small window is +/// used to calculate the determinant and trace of such a window, from which a response is +/// calculated. Pixels are considered corners if they are local maximas and have a high positive +/// response. +/// +/// # Parameters +/// +/// - `input` is the array containing a grayscale image (color images are not supported) +/// - `max_corners` is the maximum number of corners to keep, only retains those with highest Harris responses +/// - `min_response` is the minimum response in order for a corner to be retained, only used if max_corners = 0 +/// - `sigma` is the standard deviation of a circular window (its dimensions will be calculated according to the standard deviation), the covariation matrix will be calculated to a circular neighborhood of this standard deviation (only used when block_size == 0, must be >= 0.5f and <= 5.0f) +/// - `block_size` is square window size, the covariation matrix will be calculated to a square neighborhood of this size (must be >= 3 and <= 31) +/// - `k_thr` is the Harris constant, usually set empirically to 0.04f (must be >= 0.01f) +/// +/// # Return Values +/// +/// This function returns an object of struct [Features](./struct.Features.html) containing Arrays +/// for x and y coordinates and score, while array oreientation & size are set to 0 & 1, +/// respectively, since harris doesn't compute that information +#[allow(unused_mut)] +pub fn harris(input: &Array, max_corners: u32, min_response: f32, sigma: f32, block_size: u32, k_thr: f32) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_harris(&mut temp as *mut c_longlong as MutFeat, + input.get() as AfArray, max_corners as c_uint, + min_response as c_float, sigma as c_float, block_size as c_uint, + k_thr as c_float); + match err_val { + 0 => Ok(Features {feat: temp}), + _ => Err(AfError::from(err_val)), + } + } +} + /// ORB feature descriptor /// /// Extract ORB descriptors from FAST features that hold higher Harris responses. FAST does not @@ -260,6 +305,53 @@ pub fn hamming_matcher(query: &Array, train: &Array, } } +/// Nearest Neighbour. +/// +/// Calculates nearest distances between two 2-dimensional arrays containing features based on the +/// type of distance computation chosen. Currently, AF_SAD (sum of absolute differences), AF_SSD +/// (sum of squared differences) and AF_SHD (hamming distance) are supported. One of the arrays +/// containing the training data and the other the query data. One of the dimensions of the both +/// arrays must be equal among them, identifying the length of each feature. The other dimension +/// indicates the total number of features in each of the training and query arrays. Two +/// 1-dimensional arrays are created as results, one containg the smallest N distances of the query +/// array and another containing the indices of these distances in the training array. The resulting +/// 1-dimensional arrays have length equal to the number of features contained in the query array. +/// +/// # Parameters +/// +/// - `query` is the array containing the data to be queried +/// - `train` is the array containing the data used as training data +/// - `dist_dim` indicates the dimension to analyze for distance (the dimension indicated here must be of equal length for both query and train arrays) +/// - `n_dist` is the number of smallest distances to return (currently, only 1 is supported) +/// - `dist_type` is the distance computation type. Currently [`MatchType::SAD`](./enum.MatchType.html), [`MatchType::SSD`](./enum.MatchType.html), and [`MatchType::SHD`](./enum.MatchType.html) are supported. +/// +/// # Return Values +/// +/// A tuple of Arrays. +/// +/// The first Array is is an array of MxN size, where M is equal to the number of query features +/// and N is equal to `n_dist`. The value at position IxJ indicates the index of the Jth smallest +/// distance to the Ith query value in the train data array. the index of the Ith smallest distance +/// of the Mth query. +/// +/// The second Array is is an array of MxN size, where M is equal to the number of query features +/// and N is equal to `n_dist`. The value at position IxJ indicates the distance of the Jth smallest +/// distance to the Ith query value in the train data array based on the `dist_type` chosen. +#[allow(unused_mut)] +pub fn nearest_neighbour(query: &Array, train: &Array, dist_dim: i64, n_dist: u32, dist_type: MatchType) -> Result<(Array, Array), AfError> { + unsafe { + let mut idx: i64 = 0; + let mut dist: i64 = 0; + let err_val = af_nearest_neighbour(&mut idx as MutAfArray, &mut dist as MutAfArray, + query.get() as AfArray, train.get() as AfArray, + dist_dim as DimT, n_dist as c_uint, dist_type as c_int); + match err_val { + 0 => Ok((Array::from(idx), Array::from(dist))), + _ => Err(AfError::from(err_val)), + } + } +} + /// Image matching /// /// Template matching is an image processing technique to find small patches of an image which @@ -288,3 +380,84 @@ pub fn match_template(search_img: &Array, template_img: &Array, } } } + +/// SUSAN corner detector. +/// +/// SUSAN is an acronym standing for Smallest Univalue Segment Assimilating Nucleus. This method +/// places a circular disc over the pixel to be tested (a.k.a nucleus) to compute the corner +/// measure of that corresponding pixel. The region covered by the circular disc is M, and a pixel +/// in this region is represented by m⃗ ∈M where m⃗ 0 is the nucleus. Every pixel in the region is +/// compared to the nucleus using the following comparison function: +/// +/// c(m⃗ )=e^−((I(m⃗)−I(m⃗_0))/t)^6 +/// +/// where t is radius of the region, I is the brightness of the pixel. +/// +/// Response of SUSAN operator is given by the following equation: +/// +/// R(M) = g−n(M) if n(M) < g +/// +/// R(M) = 0 otherwise, +/// +/// where n(M)=∑c(m⃗) m⃗∈M, g is named the geometric threshold and n is the number of pixels in the +/// mask which are within t of the nucleus. +/// +/// Importance of the parameters, t and g is explained below: +/// +/// - t determines how similar points have to be to the nucleusbefore they are considered to be a +/// part of the univalue segment +/// - g determines the minimum size of the univalue segment. For a large enough g, SUSAN operator +/// becomes an edge dectector. +/// +/// # Parameters +/// +/// - `input` is input grayscale/intensity image +/// - `radius` is the nucleus radius for each pixel neighborhood +/// - `diff_thr` is intensity difference threshold a.k.a **t** from equations in description +/// - `geom_thr` is the geometric threshold +/// - `feature_ratio` is maximum number of features that will be returned by the function +/// - `edge` indicates how many pixels width area should be skipped for corner detection +/// +/// # Return Values +/// An object of type [Features](./struct.Features.html) composed of arrays for x and y coordinates, score, orientation and size of selected features. +#[allow(unused_mut)] +pub fn susan(input: &Array, radius: u32, diff_thr: f32, geom_thr: f32, feature_ratio: f32, edge: u32) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_susan(&mut temp as *mut c_longlong as MutFeat, + input.get() as AfArray, radius as c_uint, + diff_thr as c_float, geom_thr as c_float, feature_ratio as c_float, + edge as c_uint); + match err_val { + 0 => Ok(Features {feat: temp}), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Difference of Gaussians. +/// +/// Given an image, this function computes two different versions of smoothed input image using the +/// difference smoothing parameters and subtracts one from the other and returns the result. +/// +/// # Parameters +/// +/// - `input` is the input image +/// - `radius1` is the radius of the first gaussian kernel +/// - `radius2` is the radius of the second gaussian kernel +/// +/// # Return Values +/// +/// Difference of smoothed inputs - An Array. +#[allow(unused_mut)] +pub fn dog(input: &Array, radius1: i32, radius2: i32) -> Result { + unsafe { + let mut temp: i64 = 0; + let err_val = af_dog(&mut temp as MutAfArray, input.get() as AfArray, + radius1 as c_int, radius2 as c_int); + match err_val { + 0 => Ok(Array::from(temp)), + _ => Err(AfError::from(err_val)), + } + } +}