diff --git a/CHANGELOG.md b/CHANGELOG.md index c078121..cdeadf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,29 +1,25 @@ # Change Log -## [Unreleased] - +## [0.5.0] ### Added - - The `ThresholdApplyExt` trait to apply user-defined threshold - The `threshold_apply` method to the `ArrayBase` and `Image` types -## [0.4.0] 2022-02-17 - ### Changed +- Completely revamped transform module adding a new `Transform` and `ComposedTransform` +trait and fixing implementation issues +## [0.4.0] 2022-02-17 +### Changed - Remove discrete levels - this overflowed with the 64 and 128 bit types ## [0.3.0] 2021-11-24 - ### Changed - - Fixed orientation of sobel filters - Fixed remove limit on magnitude in sobel magnitude calculation ## [0.2.0] 2020-06-06 - ### Added - - Padding strategies (`NoPadding`, `ConstantPadding`, `ZeroPadding`) - Threshold module with Otsu and Mean threshold algorithms - Image transformations and functions to create affine transform matrices @@ -32,7 +28,6 @@ - Morphology module with dilation, erosion, union and intersection of binary images ### Changed - - Integrated Padding strategies into convolutions - Updated `ndarray-stats` to 0.2.0 adding `noisy_float` for median change - [INTERNAL] Disabled code coverage due to issues with tarpaulin and native libraries @@ -42,15 +37,11 @@ - Various performance enhancements in convolution and canny functions ## [0.1.1] - 2019-07-31 - ### Changed - - Applied zero padding by default in convolutions ## [0.1.0] - 2019-03-24 - ### Added - - Image type - Colour Models (RGB, Gray, HSV, CIEXYZ, Channel-less) - Histogram equalisation diff --git a/Cargo.toml b/Cargo.toml index e3f6176..2139164 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ndarray-vision" -version = "0.4.0" +version = "0.5.0" authors = ["xd009642 "] description = "A computer vision library built on top of ndarray" repository = "https://github.com/xd009642/ndarray-vision" diff --git a/examples/basic.rs b/examples/basic.rs index f3f02db..41b104c 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -3,12 +3,12 @@ use ndarray_vision::core::*; use ndarray_vision::format::netpbm::*; use ndarray_vision::format::*; use ndarray_vision::processing::*; -use std::env::current_exe; + use std::path::{Path, PathBuf}; fn main() { let root = Path::new(env!("CARGO_MANIFEST_DIR")); - let mut cameraman = root.clone().join("images/cameraman.ppm"); + let cameraman = root.clone().join("images/cameraman.ppm"); println!("{:?}", cameraman); let decoder = PpmDecoder::default(); @@ -21,7 +21,7 @@ fn main() { let mut image: Image = image.into_type(); - let _ = image + image .conv2d_inplace(boxkern.view()) .expect("Poorly sized kernel"); // There's no u8: From so I've done this to hack things diff --git a/examples/transforms.rs b/examples/transforms.rs index 42f4053..3513f10 100644 --- a/examples/transforms.rs +++ b/examples/transforms.rs @@ -42,7 +42,7 @@ fn main() { // save let path = Path::new("transformed_cameraman.png"); let file = File::create(path).expect("Couldn't create output file"); - let ref mut w = BufWriter::new(file); + let w = &mut BufWriter::new(file); let mut encoder = png::Encoder::new(w, transformed.cols() as u32, transformed.rows() as u32); encoder.set_color(png::ColorType::RGB); diff --git a/src/core/colour_models.rs b/src/core/colour_models.rs index 41d34c5..8f51a7e 100644 --- a/src/core/colour_models.rs +++ b/src/core/colour_models.rs @@ -90,8 +90,8 @@ fn rescale_pixel(x: f64) -> T where T: FromPrimitive + Num + NumCast + PixelBound + Display, { - let tmax = T::max_pixel().to_f64().unwrap_or_else(|| 0.0f64); - let tmin = T::min_pixel().to_f64().unwrap_or_else(|| 0.0f64); + let tmax = T::max_pixel().to_f64().unwrap_or(0.0f64); + let tmin = T::min_pixel().to_f64().unwrap_or(0.0f64); let x = x * (tmax - tmin) + tmin; @@ -159,17 +159,17 @@ where let x = c * (1.0f64 - ((h_deg / 60.0f64) % 2.0f64 - 1.0f64).abs()); let m = v_norm - c; - let rgb = if 0.0f64 <= h_deg && h_deg < 60.0f64 { + let rgb = if (0.0f64..60.0f64).contains(&h_deg) { (c, x, 0.0f64) - } else if 60.0f64 <= h_deg && h_deg < 120.0f64 { + } else if (60.0f64..120.0f64).contains(&h_deg) { (x, c, 0.0f64) - } else if 120.0f64 <= h_deg && h_deg < 180.0f64 { + } else if (120.0f64..180.0f64).contains(&h_deg) { (0.0f64, c, x) - } else if 180.0f64 <= h_deg && h_deg < 240.0f64 { + } else if (180.0f64..240.0f64).contains(&h_deg) { (0.0f64, x, c) - } else if 240.0f64 <= h_deg && h_deg < 300.0f64 { + } else if (240.0f64..300.0f64).contains(&h_deg) { (x, 0.0f64, c) - } else if 300.0f64 <= h_deg && h_deg < 360.0f64 { + } else if (300.0f64..360.0f64).contains(&h_deg) { (c, 0.0f64, x) } else { (0.0f64, 0.0f64, 0.0f64) diff --git a/src/core/image.rs b/src/core/image.rs index 69f19ab..915cb11 100644 --- a/src/core/image.rs +++ b/src/core/image.rs @@ -43,7 +43,7 @@ where let scaled = normalise_pixel_value(*x) * (T2::max_pixel() - T2::min_pixel()) .to_f64() - .unwrap_or_else(|| 0.0f64); + .unwrap_or(0.0f64); T2::from_f64(scaled).unwrap_or_else(T2::zero) + T2::min_pixel() }; let data = self.data.map(rescale); @@ -204,8 +204,8 @@ where let numerator = (t + T::min_pixel()).to_f64(); let denominator = (T::max_pixel() - T::min_pixel()).to_f64(); - let numerator = numerator.unwrap_or_else(|| 0.0f64); - let denominator = denominator.unwrap_or_else(|| 1.0f64); + let numerator = numerator.unwrap_or(0.0f64); + let denominator = denominator.unwrap_or(1.0f64); numerator / denominator } diff --git a/src/format/netpbm.rs b/src/format/netpbm.rs index a87e6f5..f8c4b3c 100644 --- a/src/format/netpbm.rs +++ b/src/format/netpbm.rs @@ -100,7 +100,7 @@ impl PpmEncoder { U: Data, T: Copy + Clone + Num + NumAssignOps + NumCast + PartialOrd + Display + PixelBound, { - let max_val = Self::get_max_value(image).unwrap_or_else(|| 255); + let max_val = Self::get_max_value(image).unwrap_or(255); let mut result = self .generate_header(image.rows(), image.cols(), max_val) diff --git a/src/processing/canny.rs b/src/processing/canny.rs index bf56327..92d1171 100644 --- a/src/processing/canny.rs +++ b/src/processing/canny.rs @@ -102,7 +102,7 @@ where let mut dir = rotations[[i as usize, j, 0]] .to_degrees() .to_f64() - .unwrap_or_else(|| 0.0); + .unwrap_or(0.0); let j = j as isize; if dir >= 180.0 { @@ -197,6 +197,15 @@ where result } +impl Default for CannyBuilder +where + T: Copy + Clone + FromPrimitive + Real + Num, +{ + fn default() -> Self { + Self::new() + } +} + impl CannyBuilder where T: Copy + Clone + FromPrimitive + Real + Num, @@ -267,9 +276,7 @@ where None => T::from_f64(0.7).unwrap(), }; if t2 < t1 { - let temp = t1; - t1 = t2; - t2 = temp; + std::mem::swap(&mut t1, &mut t2); } CannyParameters { blur, t1, t2 } } diff --git a/src/processing/conv.rs b/src/processing/conv.rs index fd34f71..873538d 100644 --- a/src/processing/conv.rs +++ b/src/processing/conv.rs @@ -269,7 +269,7 @@ mod tests { } #[test] - #[cfg_attr(rustfmt, rustfmt_skip)] + #[rustfmt::skip] fn basic_conv() { let input_pixels = vec![ 1, 1, 1, 0, 0, @@ -300,7 +300,7 @@ mod tests { } #[test] - #[cfg_attr(rustfmt, rustfmt_skip)] + #[rustfmt::skip] fn basic_conv_inplace() { let input_pixels = vec![ 1, 1, 1, 0, 0, diff --git a/src/processing/filter.rs b/src/processing/filter.rs index a73760a..f470d30 100644 --- a/src/processing/filter.rs +++ b/src/processing/filter.rs @@ -37,9 +37,9 @@ where Zip::indexed(self.windows(region)).for_each(|(i, j, k), window| { let mut flat_window = Array::from_iter(window.iter()).mapv(|x| *x); if let Ok(v) = flat_window.quantile_mut(n64(0.5f64), &Linear {}) { - result - .get_mut([i + r_offset, j + c_offset, k]) - .map(|r| *r = v); + if let Some(r) = result.get_mut([i + r_offset, j + c_offset, k]) { + *r = v; + } } }); result diff --git a/src/processing/kernels.rs b/src/processing/kernels.rs index 07b6ecc..dc56300 100644 --- a/src/processing/kernels.rs +++ b/src/processing/kernels.rs @@ -75,14 +75,14 @@ where let res = match p { LaplaceType::Standard => { let m_1 = -T::one(); - let p_4 = T::from_u8(4).ok_or_else(|| Error::NumericError)?; + let p_4 = T::from_u8(4).ok_or(Error::NumericError)?; let z = T::zero(); arr2(&[[z, m_1, z], [m_1, p_4, m_1], [z, m_1, z]]) } LaplaceType::Diagonal => { let m_1 = -T::one(); - let p_8 = T::from_u8(8).ok_or_else(|| Error::NumericError)?; + let p_8 = T::from_u8(8).ok_or(Error::NumericError)?; arr2(&[[m_1, m_1, m_1], [m_1, p_8, m_1], [m_1, m_1, m_1]]) } @@ -219,7 +219,7 @@ where /// Build a fixed size kernel with the given parameters fn build_with_params(p: Self::Params) -> Result, Error> { - let two = T::from_i8(2).ok_or_else(|| Error::NumericError)?; + let two = T::from_i8(2).ok_or(Error::NumericError)?; // Gets the gradient along the horizontal axis #[rustfmt::skip] let horz_sobel = arr2(&[ diff --git a/src/processing/threshold.rs b/src/processing/threshold.rs index a7061af..87bb01c 100644 --- a/src/processing/threshold.rs +++ b/src/processing/threshold.rs @@ -147,8 +147,8 @@ where sum_intensity += (index as f64) * (*count).to_f64().unwrap(); } for (index, count) in counts.indexed_iter() { - weight_b = weight_b + count.to_f64().unwrap(); - sum_b = sum_b + (index as f64) * count.to_f64().unwrap(); + weight_b += count.to_f64().unwrap(); + sum_b += (index as f64) * count.to_f64().unwrap(); let weight_f = total - weight_b; if (weight_b > 0.0) && (weight_f > 0.0) { let mean_f = (sum_intensity - sum_b) / weight_f; @@ -162,7 +162,7 @@ where } } } - threshold = level as f64 / scale_factor; + threshold = level / scale_factor; } Ok(threshold) } diff --git a/src/transform/affine.rs b/src/transform/affine.rs index fe9be30..7a295be 100644 --- a/src/transform/affine.rs +++ b/src/transform/affine.rs @@ -4,19 +4,18 @@ use ndarray_linalg::Inverse; /// converts a matrix into an equivalent `AffineTransform` pub fn transform_from_2dmatrix(in_array: Array2) -> AffineTransform { - let transform = match in_array.inv() { + match in_array.inv() { Ok(inv) => AffineTransform { - matrix2d_transform: in_array.clone(), + matrix2d_transform: in_array, matrix2d_transform_inverse: inv, inverse_exists: true, }, - Err(e) => AffineTransform { - matrix2d_transform: in_array.clone(), + Err(_e) => AffineTransform { + matrix2d_transform: in_array, matrix2d_transform_inverse: Array2::zeros((2, 2)), inverse_exists: false, }, - }; - return transform; + } } /// a linear transform of an image represented by either size 2x2 @@ -60,7 +59,7 @@ impl Transform for AffineTransform { } fn inverse_exists(&self) -> bool { - return self.inverse_exists; + self.inverse_exists } } diff --git a/src/transform/mod.rs b/src/transform/mod.rs index 8656c73..66bbd22 100644 --- a/src/transform/mod.rs +++ b/src/transform/mod.rs @@ -1,8 +1,6 @@ use crate::core::{ColourModel, Image, ImageBase}; -use ndarray::{array, prelude::*, s, Data}; -use ndarray_linalg::*; +use ndarray::{prelude::*, s, Data}; use num_traits::{Num, NumAssignOps}; -use std::cmp::{max, min}; use std::fmt::Display; pub mod affine; @@ -18,9 +16,9 @@ impl std::error::Error for TransformError {} impl Display for TransformError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - TransformError::InvalidTransform => return write!(f, "invalid transform"), + TransformError::InvalidTransform => write!(f, "invalid transform"), TransformError::NonInvertibleTransform => { - return write!( + write!( f, "Non Invertible Transform, Forward transform not yet implemented " ) @@ -46,17 +44,16 @@ pub struct ComposedTransform { impl Transform for ComposedTransform { fn apply(&self, p: (f64, f64)) -> (f64, f64) { - return self.transform2.apply(self.transform1.apply(p)); + self.transform2.apply(self.transform1.apply(p)) } fn apply_inverse(&self, p: (f64, f64)) -> (f64, f64) { - return self - .transform1 - .apply_inverse(self.transform2.apply_inverse(p)); + self.transform1 + .apply_inverse(self.transform2.apply_inverse(p)) } fn inverse_exists(&self) -> bool { - return self.transform1.inverse_exists() && self.transform2.inverse_exists(); + self.transform1.inverse_exists() && self.transform2.inverse_exists() } } @@ -85,30 +82,6 @@ struct Rect { h: usize, } -fn bounding_box(dims: (f64, f64), transform: T) -> Rect { - let tl = transform.apply((0.0, 0.0)); - let tr = transform.apply((0.0, dims.1)); - let br = transform.apply(dims); - let bl = transform.apply((dims.0, 0.0)); - - let tl = (tl.0.round() as isize, tl.1.round() as isize); - let tr = (tr.0.round() as isize, tr.1.round() as isize); - let br = (br.0.round() as isize, br.1.round() as isize); - let bl = (bl.0.round() as isize, bl.1.round() as isize); - - let leftmost = min(min(tl.0, tr.0), min(br.0, bl.0)); - let topmost = min(min(tl.1, tr.1), min(br.1, bl.1)); - let rightmost = max(max(tl.0, tr.0), max(br.0, bl.0)); - let bottommost = max(max(tl.1, tr.1), max(br.1, bl.1)); - - Rect { - x: leftmost, - y: topmost, - w: (rightmost - leftmost) as usize, - h: (bottommost - topmost) as usize, - } -} - impl TransformExt for ArrayBase where T: Copy + Clone + Num + NumAssignOps,