Using Rust for Preprocessing images

Rust is an ideal choice for image preprocessing due to its combination of performance, safety, and concurrency. Its low-level control allows for highly efficient image manipulation, crucial for handling large datasets or performing complex transformations.

Image preprocessing involves transforming raw image data into a format that is usable and meaningful.

If you want a fast Linux VPS server with Python installed check out :

🟩 https://webdock.io/en?maff=wdaff–170

Rust-Image-Preprocessing

Rust code to resize and convert images for ML

[dependencies]
image = "0.24.6"
ndarray = "0.15.4"

Imports

use image::{imageops::FilterType, GrayImage, Luma};
use ndarray::Array2;
use std::error::Error;
use std::path::Path;
use std::fs;

This section includes necessary imports. image for image processing, ndarray for working with arrays, and standard libraries for error handling, file system operations, and path manipulation.

Image Preprocessing Function

// Function to read and preprocess the image
fn preprocess_image(
    image_path: &str,
    target_width: u32,
    target_height: u32,
) -> Result<Array2<f32>, Box<dyn Error>> {
    // Load the image
    let img = image::open(image_path)?;

    // Resize the image to the target dimensions
    let resized_img = img.resize_exact(target_width, target_height, FilterType::Nearest);

    // Convert the image to grayscale
    let grey_img = resized_img.to_luma8(); // to_luma8 converts the image to grayscale (Luma)

    // Convert image to a Vec<u8> and then to a 2D ndarray
    let (width, height) = grey_img.dimensions();
    let pixels: Vec<f32> = grey_img.iter().map(|&p| p as f32 / 255.0).collect(); // Normalize to [0.0, 1.0]

    // Create a 2D ndarray from the pixel data
    let array = Array2::from_shape_vec((height as usize, width as usize), pixels)?;

    Ok(array)
}

This function handles loading, resizing, converting to grayscale, normalizing, and converting an image to a 2D ndarray.

Save Processed Image Function

// Function to save the processed image
fn save_processed_image(
    image_data: &Array2<f32>,
    output_path: &str,
) -> Result<(), Box<dyn Error>> {
    let (height, width) = image_data.dim();
    let grey_img = GrayImage::from_fn(width as u32, height as u32, |x, y| {
        let pixel_value = (image_data[(y as usize, x as usize)] * 255.0).round() as u8;
        Luma([pixel_value])
    });

    // Save the processed image
    grey_img.save(output_path)?;
    Ok(())
}

Directory Check Function

fn ensure_dir_exists(dir: &str) -> Result<(), Box<dyn Error>> {
    let path = Path::new(dir);
    if !path.exists() {
        fs::create_dir_all(path)?;
        println!("The '{}' directory was created.", dir);
    }
    Ok(())
}

Main Function

fn main() -> Result<(), Box<dyn Error>> {
    // Define the directory containing images and the target dimensions
    let image_dir = "images";
    let processed_dir = "processed";
    let target_width = 224;
    let target_height = 224;

    // Ensure both 'images' and 'processed' directories exist
    ensure_dir_exists(image_dir)?;
    ensure_dir_exists(processed_dir)?;

    // Iterate over all files in the directory
    for entry in fs::read_dir(image_dir)? {
        let entry = entry?;
        let path = entry.path();

        // Process only files with image extensions
        if let Some(extension) = path.extension() {
            if extension == "jpg" || extension == "jpeg" || extension == "png" {
                // Define paths for input and output images
                let image_path = path.to_str().ok_or("Invalid image path - run from src")?;
                let output_path =
                    format!("processed/{}", path.file_name().unwrap().to_str().unwrap());

                // Ensure the 'processed' directory exists
                fs::create_dir_all("processed")?;

                // Preprocess the image
                let processed_image = preprocess_image(image_path, target_width, target_height)?;

                // Save the processed image
                save_processed_image(&processed_image, &output_path)?;

                println!("Processed image saved to: {}", output_path);
            }
        }
    }

    Ok(())
}

Summary :

This Rust code processes images by resizing and converting them to grayscale, then saving the results. It ensures the necessary directories exist and handles image files in the specified directory. Key functions include image loading and preprocessing, directory management, and saving processed images in a separate director

before

for a more advanced introduction to computer vision: 
https://rust-cv.github.io/tutorial/chapter1-introduction.html

Webdock – Fast Cloud VPS Linux Hosting