How to Configure ImageKit for Recursive Image Processing

ImageKit automatically walks through every subdirectory of the supplied input_dir and processes all supported image files by default, requiring no special flags to enable recursion.

ImageKit is a Rust-based batch image processing tool maintained in the hzbd/imagekit repository. It handles complex directory hierarchies using the walkdir crate to discover and process images recursively. This guide demonstrates how to configure both the CLI and Rust API to process nested image directories.

How Recursive Traversal Works

According to the source code in hzbd/imagekit, recursive processing is implemented in src/lib.rs using the walkdir crate. The run function initializes a recursive iterator:

let image_paths: Vec<PathBuf> = walkdir::WalkDir::new(&cli.input_dir)

This iterator traverses every subdirectory beneath the specified input path. The collection logic filters entries to include only supported formats—JPG, JPEG, PNG, GIF, BMP, and WEBP—before dispatching them to src/processor.rs. Each valid path is passed to the process_image function, which handles resizing, watermarking, and format conversion.

Because the directory walk completes before image processing begins, no additional configuration is required to enable recursion. Simply provide the top-level directory to --input-dir.

CLI Configuration Options

All recursive processing options are defined in src/cli.rs. These parameters apply uniformly to every image discovered during the directory traversal:

  • -i, --input-dir <PATH>: Root of the recursive scan (required). Example: --input-dir ./photos
  • -o, --output-dir <PATH>: Destination for the processed tree, preserving hierarchy (required). Example: --output-dir ./out
  • --width <N>: Target width in pixels, with height auto-scaled. Example: --width 1200
  • --height <N>: Target height in pixels, with width auto-scaled. Example: --height 800
  • --watermark-text <STR>: Text overlay for each image. Example: --watermark-text "© MyCompany"
  • --watermark-position <POS>: Watermark location using cardinal directions (nw, north, ne, west, center, east, sw, south, se). Example: --watermark-position se
  • --font-size <N>: Base font size for watermarks. Example: --font-size 36
  • --watermark-color <HEX>: RGBA hex color (default #ffffff80). Example: --watermark-color "#ff0000ff"
  • -q, --quality <1-100>: JPEG/PNG output quality (default 85). Example: --quality 90
  • --output-format <FMT>: Force specific output format (jpg, png, webp, etc.). Example: --output-format png

Example Command

Run the following to process a nested directory structure:

cargo run --release -- \
  -i ./example/img-src \
  -o ./example/img-out \
  --width 1024 \
  --watermark-text "© MyBrand" \
  --watermark-position se \
  --font-size 48 \
  --watermark-color "#00ff00ff" \
  -q 92

This command recursively walks ./example/img-src, resizes images to 1024 pixels wide, applies a green watermark in the bottom-right corner, and writes results to ./example/img-out while maintaining the original subdirectory structure.

Library Usage in Rust

To configure recursive processing programmatically, construct the Cli struct and invoke run from src/lib.rs:

use imagekit::{cli::Cli, run};
use std::path::PathBuf;

fn main() -> anyhow::Result<()> {
    let cli = Cli {
        input_dir: PathBuf::from("./photos"),
        output_dir: PathBuf::from("./processed"),
        width: Some(800),
        height: None,
        watermark_text: Some("© Demo".into()),
        watermark_position: imagekit::cli::WatermarkPosition::Se,
        font_size: 32,
        watermark_color: imagekit::cli::HexColor(image::Rgba([255, 255, 255, 128])),
        quality: 85,
        output_format: None,
    };

    run(cli)
}

The run function performs the identical recursive walk as the CLI, utilizing Rayon for parallel processing of the discovered images.

Debugging the File Discovery

To verify which files are queued during recursion, add debug output to the iterator in src/lib.rs (around lines 45-55):

.filter_map(|e| {
    let entry = e.ok()?;
    if entry.path().is_file() && /* extension check */ {
        println!("Queued: {}", entry.path().display());
        Some(entry.path().to_path_buf())
    } else {
        None
    }
})

Running the binary with this modification prints every discovered image path, confirming the recursive traversal across subdirectories.

Summary

  • Recursion is automatic: ImageKit uses walkdir in src/lib.rs to traverse all subdirectories without extra flags.
  • Format filtering: Only jpg, jpeg, png, gif, bmp, and webp files are processed; others are ignored.
  • Uniform processing: All CLI options apply to every image found in the hierarchy.
  • Structure preservation: The output directory mirrors the input directory's nested layout.
  • Dual interfaces: Both the CLI (src/cli.rs) and Rust API (src/lib.rs) support identical recursive capabilities.

Frequently Asked Questions

Does ImageKit require a special flag to process subdirectories?

No. The hzbd/imagekit source code implements recursion by default. The walkdir::WalkDir iterator in src/lib.rs automatically descends into all subdirectories of the --input-dir path. No additional configuration flags are needed to enable this behavior.

How does ImageKit handle unsupported file types during recursive scans?

The traversal logic in src/lib.rs filters the walkdir results by file extension, retaining only images matching jpg, jpeg, png, gif, bmp, or webp. Non-image files and directory entries are excluded from the image_paths vector before processing begins, ensuring only compatible formats reach the process_image function in src/processor.rs.

Will the output directory maintain the same folder structure as the input?

Yes. ImageKit preserves the relative path hierarchy from the input root to the output root. A file located at ./photos/2024/vacation/image.jpg appears in the output directory at ./processed/2024/vacation/image.jpg, maintaining your organizational structure throughout the recursive batch operation.

Can I limit the recursion depth or exclude specific subdirectories?

The current implementation in src/lib.rs uses the default walkdir configuration without depth limits or exclude patterns. To restrict processing, you must either reorganize your source directory structure or manually filter the image_paths vector before it enters the parallel processing stage within the run function.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →