| Crates.io | subpixel-edge |
| lib.rs | subpixel-edge |
| version | 0.2.0 |
| created_at | 2025-07-24 18:30:04.971084+00 |
| updated_at | 2025-08-10 06:23:38.860615+00 |
| description | High-performance subpixel edge detection library with parallel processing using Canny algorithm |
| homepage | https://github.com/spartajet/subpixel-edge |
| repository | https://github.com/spartajet/subpixel-edge |
| max_upload_size | |
| id | 1766373 |
| size | 163,093 |
A high-performance Rust library for subpixel edge detection using optimized Canny algorithm with parallel processing capabilities.
This library provides state-of-the-art subpixel edge detection with significant performance improvements through parallel processing using rayon. The implementation combines the classical Canny edge detection algorithm with advanced subpixel refinement techniques to achieve both speed and accuracy.
Add this to your Cargo.toml:
[dependencies]
# Choose one of the following image processing backends:
# Option 1: Using image and imageproc crates (pure Rust)
subpixel-edge = { version = "0.2.0", features = ["imagers"] }
# Option 2: Using OpenCV (requires OpenCV installation)
subpixel-edge = { version = "0.2.0", features = ["opencv"] }
This crate requires you to choose one image processing backend:
imagers: Uses the image and imageproc crates (pure Rust implementation)
opencv: Uses OpenCV bindings
Note: These features are mutually exclusive. You cannot enable both at the same time.
In addition to the image processing backend, the crate supports:
logger: Enables debug logging output for performance monitoring and troubleshootingExample with logging enabled:
[dependencies]
# With image/imageproc backend and logging
subpixel-edge = { version = "0.2.0", features = ["imagers", "logger"] }
log = "0.4"
env_logger = "0.11"
# OR with OpenCV backend and logging
subpixel-edge = { version = "0.2.0", features = ["opencv", "logger"] }
log = "0.4"
env_logger = "0.11"
imagers featureuse image::open;
use subpixel_edge::{canny_based_subpixel_edges_optimized, visualize_edges};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Load image
let image = open("input.png")?.to_luma8();
// Detect subpixel edges
let edges = canny_based_subpixel_edges_optimized(
&image,
20.0, // low_threshold
80.0, // high_threshold
0.6 // edge_point_threshold
);
// Visualize results
let visualization = visualize_edges(&image, &edges);
visualization.save("edges_output.png")?;
println!("Detected {} subpixel edge points", edges.len());
Ok(())
}
opencv featureuse opencv::{imgcodecs, prelude::*};
use subpixel_edge::{canny_based_subpixel_edges_optimized, visualize_edges};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Load image
let image = imgcodecs::imread("input.png", imgcodecs::IMREAD_GRAYSCALE)?;
// Detect subpixel edges
let edges = canny_based_subpixel_edges_optimized(
&image,
20.0, // low_threshold
80.0, // high_threshold
0.6 // edge_point_threshold
);
// Visualize results
let visualization = visualize_edges(&image, &edges);
imgcodecs::imwrite("edges_output.png", &visualization, &opencv::core::Vector::new())?;
println!("Detected {} subpixel edge points", edges.len());
Ok(())
}
use image::open;
use subpixel_edge::{canny_based_subpixel_edges_optimized, visualize_edges};
use env_logger;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize logger to see debug output from the library
env_logger::init();
let image = open("input.png")?.to_luma8();
let edges = canny_based_subpixel_edges_optimized(&image, 20.0, 80.0, 0.6);
// With logger feature enabled, you'll see debug output like:
// DEBUG subpixel_edge: start calculate subpixel edges
// DEBUG subpixel_edge: gx_data and gy_data ok
// DEBUG subpixel_edge: mag_data ok
// DEBUG subpixel_edge: thinned ok
// DEBUG subpixel_edge: canny_edge_points ok
let visualization = visualize_edges(&image, &edges);
visualization.save("edges_output.png")?;
println!("Detected {} subpixel edge points", edges.len());
Ok(())
}
Run with logging:
cargo run --features logger
# or set log level
RUST_LOG=debug cargo run --features logger
canny_based_subpixel_edges_optimizedpub fn canny_based_subpixel_edges_optimized(
image: &GrayImage,
low_threshold: f32,
high_threshold: f32,
edge_point_threshold: f32,
) -> Vec<(f32, f32)>
Main function for subpixel edge detection.
Parameters:
image: Input grayscale imagelow_threshold: Lower threshold for hysteresis (10.0-50.0)high_threshold: Upper threshold for hysteresis (50.0-150.0)edge_point_threshold: Maximum subpixel offset (0.5-1.0)Returns: Vector of subpixel edge coordinates (x, y)
parallel_sobel_gradientspub fn parallel_sobel_gradients(image: &GrayImage) -> (Vec<f32>, Vec<f32>)
Computes Sobel gradients in parallel.
Parameters:
image: Input grayscale imageReturns: Tuple of (horizontal_gradients, vertical_gradients)
visualize_edgespub fn visualize_edges(image: &GrayImage, edge_points: &[(f32, f32)]) -> RgbImage
Creates visualization of detected edges.
Parameters:
image: Original grayscale imageedge_points: Detected subpixel edge coordinatesReturns: RGB image with edges highlighted in red
Low Threshold (10.0 - 50.0)
High Threshold (50.0 - 150.0)
Edge Point Threshold (0.3 - 1.0)
High Noise Images:
let edges = canny_based_subpixel_edges_optimized(&image, 30.0, 90.0, 0.5);
Clean Images:
let edges = canny_based_subpixel_edges_optimized(&image, 15.0, 60.0, 0.8);
Fine Detail Detection:
let edges = canny_based_subpixel_edges_optimized(&image, 10.0, 40.0, 0.7);
On a typical 2800x1600 image with modern multi-core CPU:
use image::open;
use subpixel_edge::canny_based_subpixel_edges_optimized;
let image = open("photo.jpg")?.to_luma8();
let edges = canny_based_subpixel_edges_optimized(&image, 20.0, 80.0, 0.6);
for (x, y) in edges.iter().take(10) {
println!("Edge at: ({:.3}, {:.3})", x, y);
}
use std::fs;
use image::open;
use subpixel_edge::canny_based_subpixel_edges_optimized;
fn process_directory(input_dir: &str) -> Result<(), Box<dyn std::error::Error>> {
for entry in fs::read_dir(input_dir)? {
let path = entry?.path();
if let Some(ext) = path.extension() {
if ext == "png" || ext == "jpg" {
let image = open(&path)?.to_luma8();
let edges = canny_based_subpixel_edges_optimized(&image, 20.0, 80.0, 0.6);
println!("{}: {} edges", path.display(), edges.len());
}
}
}
Ok(())
}
use image::{Rgb, RgbImage};
use subpixel_edge::{canny_based_subpixel_edges_optimized, visualize_edges};
fn custom_edge_overlay(
image: &image::GrayImage,
edges: &[(f32, f32)]
) -> RgbImage {
let mut canvas = visualize_edges(image, edges);
// Add additional markers for strong edges
for &(x, y) in edges.iter() {
let ix = x as u32;
let iy = y as u32;
if ix > 0 && iy > 0 && ix < canvas.width()-1 && iy < canvas.height()-1 {
// Add cross marker
canvas.put_pixel(ix-1, iy, Rgb([255, 255, 0]));
canvas.put_pixel(ix+1, iy, Rgb([255, 255, 0]));
canvas.put_pixel(ix, iy-1, Rgb([255, 255, 0]));
canvas.put_pixel(ix, iy+1, Rgb([255, 255, 0]));
}
}
canvas
}
loggerEnables debug logging throughout the edge detection pipeline. When enabled, the library will output detailed debug information including:
Usage:
[dependencies]
subpixel-edge = { version = "0.1.0", features = ["logger"] }
Example output with logging enabled:
DEBUG subpixel_edge: start calculate subpixel edges
DEBUG subpixel_edge: gx_data and gy_data ok
DEBUG subpixel_edge: gx_image and gy_image ok
DEBUG subpixel_edge: mag_data ok
DEBUG subpixel_edge: points len:571704
DEBUG subpixel_edge: thinned ok
DEBUG subpixel_edge: canny_edge_points ok
logger feature)This project is dual-licensed under either:
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
git clone https://github.com/spartajet/subpixel-edge
cd subpixel-edge
cargo build
cargo test
cargo run --features logger --example subpixel_edge_detection
This crate is published on crates.io.
To publish a new version:
cargo publish --dry-run
cargo publish