From a62b58b5299a668e3d273cb6c3ed40097d266205 Mon Sep 17 00:00:00 2001 From: Marc Haubenstock Date: Sun, 28 Jul 2019 13:39:43 +0200 Subject: [PATCH] convolution for static and dynamic --- src/linalg/convolution.rs | 106 ++++++++++++++++++++++++++++++------ tests/linalg/convolution.rs | 49 +++++++++-------- 2 files changed, 117 insertions(+), 38 deletions(-) diff --git a/src/linalg/convolution.rs b/src/linalg/convolution.rs index 867d414c..60798b6c 100644 --- a/src/linalg/convolution.rs +++ b/src/linalg/convolution.rs @@ -2,9 +2,9 @@ use std::cmp; use crate::base::allocator::Allocator; use crate::base::default_allocator::DefaultAllocator; -use crate::base::dimension::{Dim, DimAdd, DimDiff, DimSub, DimSum}; +use crate::base::dimension::{Dim, DimAdd, DimDiff, DimSub, DimSum, DimName, DimMul}; use crate::storage::Storage; -use crate::{zero, RealField, Vector, VectorN, U1, Scalar, Matrix, DMatrix}; +use crate::{zero, RealField, Vector, VectorN, U1, Scalar, Matrix, MatrixMN, DMatrix}; use crate::alga::general::Field; impl> Vector { @@ -130,7 +130,7 @@ impl> Vector { } } -// TODO: @Investigate -> Only implemented for DMatrix for now as images are usually DMatrix + impl DMatrix { /// Returns the convolution of the target vector and a kernel. /// @@ -141,7 +141,7 @@ impl DMatrix { /// # Errors /// Inputs must satisfy `self.shape() >= kernel.shape() > 0`. /// - pub fn mat_convolve_full( + pub fn dmat_convolve_full( &self, kernel: Matrix, //TODO: Would be nice to have an IsOdd trait. As kernels could be of even size atm ) -> DMatrix @@ -150,10 +150,10 @@ impl DMatrix { C1: Dim, S1: Storage { - let mat_rows = self.nrows(); - let mat_cols = self.ncols(); - let ker_rows = kernel.data.shape().0.value(); - let ker_cols = kernel.data.shape().1.value(); + let mat_rows = self.nrows() as i32; + let mat_cols = self.ncols() as i32; + let ker_rows = kernel.data.shape().0.value() as i32; + let ker_cols = kernel.data.shape().1.value() as i32; if ker_rows == 0 || ker_rows > mat_rows || ker_cols == 0|| ker_cols > mat_cols { panic!( @@ -166,29 +166,30 @@ impl DMatrix { let kernel_size = ker_rows; let kernel_min = kernel_size/2; let zero = zero::(); - let mut conv = DMatrix::from_element(mat_cols, mat_rows, zero); + let mut conv = DMatrix::::zeros(mat_cols as usize, mat_rows as usize); for i in 0..mat_rows { for j in 0..mat_cols { for k_i in 0..kernel_size { for k_j in 0..kernel_size { - let i_matrix = (i + k_i - kernel_min) as i32; - let j_matrix = (j + k_j - kernel_min) as i32; + let i_matrix = i + k_i - kernel_min; + let j_matrix = j + k_j - kernel_min; - let is_i_in_range = i_matrix >=0 && i_matrix < mat_rows as i32; - let is_j_in_range = j_matrix >=0 && j_matrix < mat_cols as i32; + let is_i_in_range = i_matrix >=0 && i_matrix < mat_rows; + let is_j_in_range = j_matrix >=0 && j_matrix < mat_cols; let convolved_value = match is_i_in_range && is_j_in_range { true => { let pixel_value = *self.index((i_matrix as usize, j_matrix as usize)); - let kernel_value = *kernel.index((k_i,k_j)); + let kernel_value = *kernel.index((k_i as usize,k_j as usize)); kernel_value*pixel_value } + //TODO: More behaviour on borders false => zero }; - *conv.index_mut((i,j)) = convolved_value; + *conv.index_mut((i as usize,j as usize)) += convolved_value; } } @@ -198,7 +199,80 @@ impl DMatrix { conv } - //TODO: rest + //TODO: rest ? + + +} + +impl MatrixMN where DefaultAllocator: Allocator { + /// Returns the convolution of the target vector and a kernel. + /// + /// # Arguments + /// + /// * `kernel` - A Matrix with rows > 0 and cols > 0 + /// + /// # Errors + /// Inputs must satisfy `self.shape() >= kernel.shape() > 0`. + /// + pub fn smat_convolve_full( + &self, + kernel: Matrix, //TODO: Would be nice to have an IsOdd trait. As kernels could be of even size atm + ) -> MatrixMN + where + R2: Dim, + C2: Dim, + S1: Storage + { + let mat_rows = self.nrows() as i32; + let mat_cols = self.ncols() as i32; + let ker_rows = kernel.data.shape().0.value() as i32; + let ker_cols = kernel.data.shape().1.value() as i32; + + if ker_rows == 0 || ker_rows > mat_rows || ker_cols == 0|| ker_cols > mat_cols { + panic!( + "convolve_full expects `self.nrows() >= kernel.nrows() > 0 and self.ncols() >= kernel.ncols() > 0 `, \ + rows received {} and {} respectively. \ + cols received {} and {} respectively.", + mat_rows, ker_rows, mat_cols, ker_cols); + } + + let kernel_size = ker_rows; + let kernel_min = kernel_size/2; + let zero = zero::(); + let mut conv = MatrixMN::::zeros(); + + for i in 0..mat_rows { + for j in 0..mat_cols { + for k_i in 0..kernel_size { + for k_j in 0..kernel_size { + let i_matrix = i + k_i - kernel_min; + let j_matrix = j + k_j - kernel_min; + + let is_i_in_range = i_matrix >=0 && i_matrix < mat_rows; + let is_j_in_range = j_matrix >=0 && j_matrix < mat_cols; + + let convolved_value = + match is_i_in_range && is_j_in_range { + true => { + let pixel_value = *self.index((i_matrix as usize, j_matrix as usize)); + let kernel_value = *kernel.index((k_i as usize,k_j as usize)); + kernel_value*pixel_value + } + //TODO: More behaviour on borders + false => zero + }; + + *conv.index_mut((i as usize,j as usize)) += convolved_value; + } + } + + } + } + + conv + } + + //TODO: rest ? } diff --git a/tests/linalg/convolution.rs b/tests/linalg/convolution.rs index f4bb076c..69783cd9 100644 --- a/tests/linalg/convolution.rs +++ b/tests/linalg/convolution.rs @@ -1,4 +1,4 @@ -use na::{Vector2,Vector3,Vector4,Vector5,DVector, DMatrix}; +use na::{Vector2,Vector3,Vector4,Vector5,DVector, DMatrix, Matrix5, Matrix3}; use std::panic; // @@ -163,29 +163,34 @@ fn convolve_valid_check(){ // >>> convolve([1,2,3,4],[1,2],"same") // array([ 1, 4, 7, 10]) #[test] -fn convolve_same_dmat_check(){ +fn convolve_same_mat_check(){ + let actual_s = Matrix5::from_vec( vec![3.0,4.0,4.0,4.0,3.0,4.0,5.0,5.0,5.0,4.0,4.0,5.0,5.0,5.0,4.0,4.0,5.0,5.0,5.0,4.0,3.0,4.0,4.0,4.0,3.0]); + let expected_s = Matrix5::from_element(1.0).smat_convolve_full(Matrix3::from_vec(vec![0.0,1.0,0.0,1.0,1.0,1.0,0.0,1.0,0.0])); + + assert!(relative_eq!(actual_s, expected_s, epsilon = 1.0e-7)); + let actual_d = DMatrix::from_vec(5,5, vec![3.0,4.0,4.0,4.0,3.0,4.0,5.0,5.0,5.0,4.0,4.0,5.0,5.0,5.0,4.0,4.0,5.0,5.0,5.0,4.0,3.0,4.0,4.0,4.0,3.0]); - let expected_d = DMatrix::from_element(5,5,1.0).mat_convolve_full(DMatrix::from_vec(3,3,vec![0.0,1.0,0.0,1.0,1.0,1.0,0.0,1.0,0.0])); + let expected_d = DMatrix::from_element(5,5,1.0).dmat_convolve_full(DMatrix::from_vec(3,3,vec![0.0,1.0,0.0,1.0,1.0,1.0,0.0,1.0,0.0])); assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); -// // Panic Tests -// // These really only apply to dynamic sized vectors -// assert!( -// panic::catch_unwind(|| { -// DVector::from_vec(vec![1.0, 2.0]).convolve_same(DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0])); -// }).is_err() -// ); -// -// assert!( -// panic::catch_unwind(|| { -// DVector::::from_vec(vec![]).convolve_same(DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0])); -// }).is_err() -// ); -// -// assert!( -// panic::catch_unwind(|| { -// DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]).convolve_same(DVector::::from_vec(vec![])); -// }).is_err() -// ); + // Panic Tests + // These really only apply to dynamic sized vectors + assert!( + panic::catch_unwind(|| { + DMatrix::from_element(2,2,1.0).dmat_convolve_full(DMatrix::from_vec(3,3,vec![0.0,1.0,0.0,1.0,1.0,1.0,0.0,1.0,0.0])); + }).is_err() + ); + + assert!( + panic::catch_unwind(|| { + DMatrix::from_element(0,0,1.0).dmat_convolve_full(DMatrix::from_vec(3,3,vec![0.0,1.0,0.0,1.0,1.0,1.0,0.0,1.0,0.0])); + }).is_err() + ); + + assert!( + panic::catch_unwind(|| { + DMatrix::from_element(5,5,1.0).dmat_convolve_full(DMatrix::from_vec(0,0,vec![])); + }).is_err() + ); } \ No newline at end of file