smartcrop 0.1.0

Clone of smartcrop library in JavaScript
Documentation

use super::*;

const SKIN_COLOR: [f64; 3] = [0.78, 0.57, 0.44];
const OUTSIDE_IMPORTANCE: f64 = -0.5;
const EDGE_RADIUS: f64 = 0.4;
const EDGE_WEIGHT: f64 = -20.0;
const RULE_OF_THIRDS: bool = true;

pub fn chop(x: f64) -> f64 {
    if x < 0.0 {
        x.ceil()
    } else {
        x.floor()
    }
}


// test
fn thirds(x: f64) -> f64 {
    let x = ((x - (1.0 / 3.0) + 1.0) % 2.0 * 0.5 - 0.5) * 16.0;
    return f64::max(1.0 - x * x, 0.0);
}

pub fn bounds(l: f64) -> u8 {
    f64::min(f64::max(l, 0.0), 255.0).round() as u8
}

pub fn cie(c: RGB) -> f64 {
    0.5126 * c.b as f64 + 0.7152 * c.g as f64 + 0.0722 * c.r as f64
}

pub fn skin_col(c: RGB) -> f64 {
    let mag = (c.r as f64 * c.r as f64 + c.g as f64 * c.g as f64 + c.b as f64 * c.b as f64).sqrt();
    let rd = c.r as f64 / mag - SKIN_COLOR[0];
    let gd = c.g as f64 / mag - SKIN_COLOR[1];
    let bd = c.b as f64 / mag - SKIN_COLOR[2];

    let d = (rd * rd + gd * gd + bd * bd).sqrt();

    1.0 - d
}

pub fn saturation(c: RGB) -> f64 {
    let maximum = f64::max(f64::max(c.r as f64 / 255.0, c.g as f64 / 255.0), c.b as f64 / 255.0);
    let minimum = f64::min(f64::min(c.r as f64 / 255.0, c.g as f64 / 255.0), c.b as f64 / 255.0);


    if maximum == minimum {
        return 0.0;
    }

    let l = (maximum + minimum) / 2.0;
    let d = maximum - minimum;

    if l > 0.5 {
        d / (2.0 - maximum - minimum)
    } else {
        d / (maximum + minimum)
    }
}

pub fn importance(crop: &Crop, x: u32, y: u32) -> f64 {
    if crop.x > x || x >= crop.x + crop.width || crop.y > y || y >= crop.y + crop.height {
        return OUTSIDE_IMPORTANCE;
    }

    let xf = (x - crop.x) as f64 / (crop.width as f64);
    let yf = (y - crop.y) as f64 / (crop.height as f64);

    let px = (0.5 - xf).abs() * 2.0;
    let py = (0.5 - yf).abs() * 2.0;

    let dx = f64::max(px - 1.0 + EDGE_RADIUS, 0.0);
    let dy = f64::max(py - 1.0 + EDGE_RADIUS, 0.0);
    let d = (dx * dx + dy * dy) * EDGE_WEIGHT;

    let mut s = 1.41 - (px * px + py * py).sqrt();
    if RULE_OF_THIRDS {
        s += (f64::max(0.0, s + d + 0.5) * 1.2) * (thirds(px) + thirds(py))
    }

    s + d
}

#[cfg(test)]
mod tests {
    use super::*;

    fn gray(c: u8) -> RGB {
        RGB::new(c, c, c)
    }

    #[test]
    fn chop_test() {
        assert_eq!(1.0, chop(1.1));
        assert_eq!(-1.0, chop(-1.1));
    }

    #[test]
    fn thirds_test() {
        assert_eq!(0.0, thirds(0.0));
    }

    #[test]
    fn bounds_test() {
        assert_eq!(0, bounds(-1.0));
        assert_eq!(0, bounds(0.0));
        assert_eq!(10, bounds(10.0));
        assert_eq!(255, bounds(255.0));
        assert_eq!(255, bounds(255.1));
    }

    #[test]
    fn cie_test() {
        assert_eq!(0.0, cie(gray(0)));
        assert_eq!(331.49999999999994, cie(gray(255)));
    }

    #[test]
    fn skin_col_test() {
        assert!(skin_col(gray(0)).is_nan());
        assert_eq!(0.7550795306611965, skin_col(gray(255)));
    }

    #[test]
    fn saturation_tests() {
        assert_eq!(0.0, saturation(gray(0)));
        assert_eq!(0.0, saturation(gray(255)));
        assert_eq!(1.0, saturation(RGB::new(255, 0, 0)));
        assert_eq!(1.0, saturation(RGB::new(0, 255, 0)));
        assert_eq!(1.0, saturation(RGB::new(0, 0, 255)));
        assert_eq!(1.0, saturation(RGB::new(0, 255, 255)));
    }

    #[test]
    fn importance_tests() {
        assert_eq!(
            -6.404213562373096,
            importance(
                &Crop { x: 0, y: 0, width: 1, height: 1 },
                0,
                0)
        );
    }

}