Skip to content

Commit 3f9bcee

Browse files
committed
Merge branch 'better_padding' into develop
2 parents f229d59 + 3dd2499 commit 3f9bcee

File tree

13 files changed

+305
-72
lines changed

13 files changed

+305
-72
lines changed

CHANGELOG.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Change Log
2+
3+
## Develop Branch (Unreleased)
4+
5+
### Added
6+
* Padding strategies (`NoPadding`, `ConstantPadding`, `ZeroPadding`)
7+
8+
### Changed
9+
* Integrated Padding strategies into convolutions
10+
11+
### Removed
12+
13+
## 0.1.1
14+
15+
### Added
16+
17+
### Changed
18+
* Applied zero padding by default in convolutions
19+
20+
### Removed
21+
22+
## 0.1.0 (First release)
23+
24+
### Added
25+
* Image type
26+
* Colour Models (RGB, Gray, HSV, CIEXYZ, Channel-less)
27+
* Histogram equalisation
28+
* Image convolutions
29+
* `PixelBound` type to aid in rescaling images
30+
* Canny edge detector
31+
* `KernelBuilder` and `FixedDimensionKernelBuilder` to create kernels
32+
* Builder implementations for Sobel, Gaussian, Box Linear filter, Laplace
33+
* Median filter
34+
* Sobel Operator
35+
* PPM encoding and decoding for images

src/core/colour_models.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ pub struct HSI;
2626
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
2727
pub struct HSL;
2828
/// YCrCb represents an image as luma, red-difference chroma and blue-difference
29-
/// chroma.
29+
/// chroma.
3030
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
3131
pub struct YCrCb;
3232
/// CIE XYZ standard - assuming a D50 reference white
3333
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
3434
pub struct CIEXYZ;
3535
/// CIE LAB (also known as CIE L*a*b* or Lab) a colour model that represents
3636
/// colour as lightness, and a* and b* as the green-red and blue-yellow colour
37-
/// differences respectively. It is designed to be representative of human
37+
/// differences respectively. It is designed to be representative of human
3838
/// perception of colour
3939
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
4040
pub struct CIELAB;
@@ -917,5 +917,4 @@ mod tests {
917917
assert_eq!(large.data.slice(s![.., .., i]), zeros);
918918
}
919919
}
920-
921920
}

src/core/image.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ where
1515
/// Images are always going to be 3D to handle rows, columns and colour
1616
/// channels
1717
///
18-
/// This should allow for max compatibility with maths ops in ndarray.
18+
/// This should allow for max compatibility with maths ops in ndarray.
1919
/// Caution should be taken if performing any operations that change the
2020
/// number of channels in an image as this may cause other functionality to
2121
/// perform incorrectly. Use conversions to one of the `Generic` colour models
@@ -61,7 +61,7 @@ where
6161
}
6262
}
6363

64-
/// Given the shape of the image and a data vector create an image. If
64+
/// Given the shape of the image and a data vector create an image. If
6565
/// the data sizes don't match a zero filled image will be returned instead
6666
/// of panicking
6767
pub fn from_shape_data(rows: usize, cols: usize, data: Vec<T>) -> Self {
@@ -151,5 +151,4 @@ mod tests {
151151
arr1(&[u16::max_value(), 0, u16::max_value() / 3])
152152
);
153153
}
154-
155154
}

src/core/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
2-
/// This module deals with different colour models and conversions between
1+
/// This module deals with different colour models and conversions between
32
/// colour models.
43
pub mod colour_models;
54
/// Core image type and simple operations on it
65
pub mod image;
6+
/// Image padding operations to increase the image size
7+
pub mod padding;
78
/// Essential traits for the functionality of `ndarray-vision`
89
pub mod traits;
910

1011
pub use colour_models::*;
1112
pub use image::*;
13+
pub use padding::*;
1214
pub use traits::*;

src/core/padding.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
use crate::core::{ColourModel, Image};
2+
use ndarray::{prelude::*, s};
3+
use num_traits::identities::Zero;
4+
use std::marker::PhantomData;
5+
6+
/// Defines a method for padding the data of an image applied directly to the
7+
/// ndarray type internally. Padding is symmetric
8+
pub trait PaddingStrategy<T>
9+
where
10+
T: Copy,
11+
{
12+
/// Taking in the image data and the margin to apply to rows and columns
13+
/// returns a padded image
14+
fn pad(&self, image: ArrayView3<T>, padding: (usize, usize)) -> Array3<T>;
15+
}
16+
17+
/// Doesn't apply any padding to the image returning it unaltered regardless
18+
/// of padding value
19+
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
20+
pub struct NoPadding;
21+
22+
/// Pad the image with a constant value
23+
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
24+
pub struct ConstantPadding<T>(T)
25+
where
26+
T: Copy;
27+
28+
/// Pad the image with zeros. Uses ConstantPadding internally
29+
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
30+
pub struct ZeroPadding;
31+
32+
impl<T> PaddingStrategy<T> for NoPadding
33+
where
34+
T: Copy,
35+
{
36+
fn pad(&self, image: ArrayView3<T>, _padding: (usize, usize)) -> Array3<T> {
37+
image.to_owned()
38+
}
39+
}
40+
41+
impl<T> PaddingStrategy<T> for ConstantPadding<T>
42+
where
43+
T: Copy,
44+
{
45+
fn pad(&self, image: ArrayView3<T>, padding: (usize, usize)) -> Array3<T> {
46+
let shape = (
47+
image.shape()[0] + padding.0 * 2,
48+
image.shape()[1] + padding.1 * 2,
49+
image.shape()[2],
50+
);
51+
52+
let mut result = Array::from_elem(shape, self.0);
53+
result
54+
.slice_mut(s![
55+
padding.0..shape.0 - padding.0,
56+
padding.1..shape.1 - padding.1,
57+
..
58+
])
59+
.assign(&image);
60+
61+
result
62+
}
63+
}
64+
65+
impl<T> PaddingStrategy<T> for ZeroPadding
66+
where
67+
T: Copy + Zero,
68+
{
69+
fn pad(&self, image: ArrayView3<T>, padding: (usize, usize)) -> Array3<T> {
70+
let padder = ConstantPadding(T::zero());
71+
padder.pad(image, padding)
72+
}
73+
}
74+
75+
/// Padding extension for images
76+
pub trait PaddingExt {
77+
/// Data type for container
78+
type Data;
79+
/// Pad the object with the given padding and strategy
80+
fn pad(&self, padding: (usize, usize), strategy: &dyn PaddingStrategy<Self::Data>) -> Self;
81+
}
82+
83+
impl<T> PaddingExt for Array3<T>
84+
where
85+
T: Copy,
86+
{
87+
type Data = T;
88+
89+
fn pad(&self, padding: (usize, usize), strategy: &dyn PaddingStrategy<Self::Data>) -> Self {
90+
strategy.pad(self.view(), padding)
91+
}
92+
}
93+
94+
impl<T, C> PaddingExt for Image<T, C>
95+
where
96+
T: Copy,
97+
C: ColourModel,
98+
{
99+
type Data = T;
100+
101+
fn pad(&self, padding: (usize, usize), strategy: &dyn PaddingStrategy<Self::Data>) -> Self {
102+
Self {
103+
data: strategy.pad(self.data.view(), padding),
104+
model: PhantomData,
105+
}
106+
}
107+
}
108+
109+
#[cfg(test)]
110+
mod tests {
111+
use super::*;
112+
use crate::core::colour_models::{Gray, RGB};
113+
114+
#[test]
115+
fn constant_padding() {
116+
let i = Image::<u8, Gray>::from_shape_data(3, 3, vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
117+
118+
let p = i.pad((1, 1), &ConstantPadding(0));
119+
120+
let exp = Image::<u8, Gray>::from_shape_data(
121+
5,
122+
5,
123+
vec![
124+
0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 4, 5, 6, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0,
125+
],
126+
);
127+
assert_eq!(p, exp);
128+
129+
let p = i.pad((1, 1), &ConstantPadding(2));
130+
131+
let exp = Image::<u8, Gray>::from_shape_data(
132+
5,
133+
5,
134+
vec![
135+
2, 2, 2, 2, 2, 2, 1, 2, 3, 2, 2, 4, 5, 6, 2, 2, 7, 8, 9, 2, 2, 2, 2, 2, 2,
136+
],
137+
);
138+
assert_eq!(p, exp);
139+
140+
let p = i.pad((2, 0), &ConstantPadding(0));
141+
let z = i.pad((2, 0), &ZeroPadding {});
142+
assert_eq!(p, z);
143+
144+
let exp = Image::<u8, Gray>::from_shape_data(
145+
7,
146+
3,
147+
vec![
148+
0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
149+
],
150+
);
151+
assert_eq!(p, exp);
152+
}
153+
154+
#[test]
155+
fn no_padding() {
156+
let i = Image::<u8, RGB>::new(5, 5);
157+
let p = i.pad((10, 10), &NoPadding {});
158+
159+
assert_eq!(i, p);
160+
161+
let p = i.pad((0, 0), &NoPadding {});
162+
assert_eq!(i, p);
163+
}
164+
}

src/core/traits.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
/// corresponding methods in `u8`
2323
/// ```
2424
pub trait PixelBound {
25-
/// The minimum value a pixel can take
25+
/// The minimum value a pixel can take
2626
fn min_pixel() -> Self;
2727
/// The maximum value a pixel can take
2828
fn max_pixel() -> Self;
2929

30-
/// Returns the number of discrete levels. This is an option because it's
31-
/// deemed meaningless for types like floats which suffer from rounding
30+
/// Returns the number of discrete levels. This is an option because it's
31+
/// deemed meaningless for types like floats which suffer from rounding
3232
/// issues
3333
fn discrete_levels() -> Option<usize>;
3434
}

0 commit comments

Comments
 (0)