use super::buffer::invalid;
use super::{Chw, Hwc, Image};
use crate::{error::Result, transform::Transform};
#[derive(Clone, Debug, PartialEq)]
pub struct Normalize {
mean: Vec<f32>,
std: Vec<f32>,
}
impl Normalize {
pub fn new(mean: Vec<f32>, std: Vec<f32>) -> Result<Self> {
if mean.is_empty() || mean.len() != std.len() {
return Err(invalid(format!(
"Normalize needs as many means as stds, at least one: {} and {}",
mean.len(),
std.len()
)));
}
if std.contains(&0.0) || !mean.iter().chain(&std).all(|v| v.is_finite()) {
return Err(invalid(format!(
"Normalize needs finite values and non-zero stds: mean {mean:?}, std {std:?}"
)));
}
Ok(Self { mean, std })
}
pub fn imagenet() -> Self {
Self {
mean: vec![0.485, 0.456, 0.406],
std: vec![0.229, 0.224, 0.225],
}
}
fn per_channel(&self, channels: usize) -> Result<Vec<(f32, f32)>> {
match self.mean.len() {
1 => Ok(vec![(self.mean[0], self.std[0]); channels]),
n if n == channels => Ok(self
.mean
.iter()
.copied()
.zip(self.std.iter().copied())
.collect()),
n => Err(invalid(format!(
"Normalize of {n} channels on an image of {channels}"
))),
}
}
}
impl Transform<Image<f32, Hwc>> for Normalize {
type Output = Image<f32, Hwc>;
fn apply(&self, _index: usize, mut image: Image<f32, Hwc>) -> Result<Image<f32, Hwc>> {
let channels = self.per_channel(image.channels())?;
for pixel in image.data_mut().chunks_exact_mut(channels.len()) {
for (value, &(mean, std)) in pixel.iter_mut().zip(&channels) {
*value = (*value - mean) / std;
}
}
Ok(image)
}
}
impl Transform<Image<f32, Chw>> for Normalize {
type Output = Image<f32, Chw>;
fn apply(&self, _index: usize, mut image: Image<f32, Chw>) -> Result<Image<f32, Chw>> {
let channels = self.per_channel(image.channels())?;
let plane = image.width() * image.height();
for (values, &(mean, std)) in image.data_mut().chunks_exact_mut(plane).zip(&channels) {
for value in values {
*value = (*value - mean) / std;
}
}
Ok(image)
}
}
#[cfg(test)]
mod tests {
use super::Normalize;
use crate::image::{Chw, Hwc, Image};
use crate::transform::Transform;
#[test]
fn both_layouts_give_the_same_values() {
let norm = Normalize::new(vec![0.5, 0.0], vec![0.5, 2.0]).unwrap();
let hwc = Image::<f32, Hwc>::new(2, 1, 2, vec![1.0, 4.0, 0.0, 2.0]).unwrap();
assert_eq!(norm.apply(0, hwc).unwrap().data(), [1.0, 2.0, -1.0, 1.0]);
let chw = Image::<f32, Chw>::new(2, 1, 2, vec![1.0, 0.0, 4.0, 2.0]).unwrap();
assert_eq!(norm.apply(0, chw).unwrap().data(), [1.0, -1.0, 2.0, 1.0]);
}
#[test]
fn bad_settings_are_errors() {
assert!(Normalize::new(vec![], vec![]).is_err());
assert!(Normalize::new(vec![0.0], vec![1.0, 2.0]).is_err());
assert!(Normalize::new(vec![0.0], vec![0.0]).is_err());
assert!(Normalize::new(vec![f32::NAN], vec![1.0]).is_err());
let one = Normalize::new(vec![1.0], vec![2.0]).unwrap();
let rgb = Image::<f32>::new(1, 1, 3, vec![3.0; 3]).unwrap();
assert_eq!(one.apply(0, rgb.clone()).unwrap().data(), [1.0; 3]);
let two = Normalize::new(vec![0.0; 2], vec![1.0; 2]).unwrap();
assert!(two.apply(0, rgb).is_err());
}
}