pub fn canny(image: &GrayImage, low_threshold: f32, high_threshold: f32) -> GrayImage {
    assert!(high_threshold >= low_threshold);
    // Heavily based on the implementation proposed by wikipedia.
    // 1. Gaussian blur.
    const SIGMA: f32 = 1.4;
    let blurred = gaussian_blur_f32(image, SIGMA);

    // 2. Intensity of gradients.
    let gx = filter_clamped(&blurred, kernel::SOBEL_HORIZONTAL_3X3);
    let gy = filter_clamped(&blurred, kernel::SOBEL_VERTICAL_3X3);
    let g: Vec<f32> = gx
        .iter()
        .zip(gy.iter())
        .map(|(h, v)| (*h as f32).hypot(*v as f32))
        .collect::<Vec<f32>>();

    let g = Image::from_raw(image.width(), image.height(), g).unwrap();

    // 3. Non-maximum-suppression (Make edges thinner)
    let thinned = non_maximum_suppression(&g, &gx, &gy);

    // 4. Hysteresis to filter out edges based on thresholds.
    hysteresis(&thinned, low_threshold, high_threshold)
}
Runs the canny edge detection algorithm.

Returns a binary image where edge pixels have a value of 255
and non-edge pixels a value of 0.

# Params

- `low_threshold`: Low threshold for the hysteresis procedure.
Edges with a strength higher than the low threshold will appear
in the output image, if there are strong edges nearby.
- `high_threshold`: High threshold for the hysteresis procedure.
Edges with a strength higher than the high threshold will always
appear as edges in the output image.

The greatest possible edge strength (and so largest sensible threshold)
is`sqrt(5) * 2 * 255`, or approximately 1140.39.

This odd looking value is the result of using a standard
definition of edge strength: the strength of an edge at a point `p` is
defined to be `sqrt(dx^2 + dy^2)`, where `dx` and `dy` are the values
of the horizontal and vertical Sobel gradients at `p`.
src/edges.rs