Skip to content

alvytsk/r3sizer

Repository files navigation

r3sizer

A Rust library and CLI for image downscaling with automatic post-resize sharpness adjustment.

The sharpening strength is selected automatically by fitting a cubic model of artifact ratios and solving for a target out-of-gamut threshold — not by a generic sharpness heuristic.

Live demo — runs entirely in the browser via WebAssembly.


Quick start

cargo build --release -p r3sizer-cli

./target/release/r3sizer \
  --input photo.jpg \
  --output out.png \
  --width 800 \
  --height 600 \
  --diagnostics diag.json

Output:

Output size                 : 800x600
Sharpen mode                : lightness (CIE Y)
Sharpen model               : practical USM
Metric mode                 : relative (sharpening-added artifacts)
Artifact metric             : channel clipping ratio
Baseline artifact ratio     : 0.000000
Selected strength           : 1.8472
Target metric value         : 0.003000
Measured metric value        : 0.002987
Measured artifact ratio     : 0.002987
Budget reachable            : yes
Fit status                  : success
Crossing status             : found
Selection mode              : polynomial root

Fit quality:
  R²                        : 0.999834
  Residual sum of squares   : 1.23e-09
  Max residual              : 2.45e-05
  Min pivot                 : 3.67e+01

Robustness:
  Monotonic                 : yes
  Quasi-monotonic           : yes
  R² ok                     : yes
  Well conditioned          : yes
  LOO stable                : yes
  Max LOO root change       : 0.0312

Timing (us):
  Resize                    : 12450
  Contrast                  : 0
  Baseline                  : 890
  Probing                   : 45230
  Fit                       : 15
  Robustness                : 98
  Final sharpen             : 6120
  Clamp                     : 340
  Total                     : 65143

Use --preserve-aspect-ratio (-p) when only one dimension is known:

r3sizer -i photo.jpg -o out.png --width 800 -p

Sweep mode

Process a directory of images and produce an aggregate summary:

r3sizer \
  --sweep-dir ./photos \
  --sweep-output-dir ./out \
  --sweep-summary summary.json \
  --width 800 --height 600

The summary JSON includes per-file results (selected strength, selection mode, timing) and aggregate statistics (mean/median strength, fit success rate, selection mode histogram).


Workspace layout

crates/
  r3sizer-core/   pure processing (color, resize, sharpen, metrics, fit, solve, pipeline)
  r3sizer-io/     image I/O (PNG/JPEG load/save via the `image` crate)
  r3sizer-cli/    command-line interface
  r3sizer-wasm/   WebAssembly bindings for browser use
web/              React/Vite diagnostic UI (talks to r3sizer-wasm via Web Worker)

r3sizer-core has no I/O or CLI dependencies and can be embedded in a Tauri GUI or compiled to WASM without modification.


Algorithm summary

input (sRGB file)
  ↓ load + normalize
  ↓ sRGB → linear RGB  (IEC 61966-2-1)
  ↓ downscale (Lanczos3 or content-adaptive kernel)
  ↓ classify regions (Flat, Textured, StrongEdge, Microtexture, RiskyHaloZone)
  ↓ optional contrast leveling
  ↓ measure baseline artifact ratio P(base)
  ↓ extract CIE Y luminance, build per-pixel gain map
  ↓ two-pass adaptive probing:
      coarse scan → find P0 crossing → dense refinement
      for each s_i:
        sharpen luminance(s_i) × gain  →  reconstruct RGB  →  chroma guard  →  measure P(s_i)
  ↓ fit cubic  P_hat(s) = a·s³ + b·s² + c·s + d  (with fit quality: R², residuals)
  ↓ robustness checks  (monotonicity, LOO stability, R², condition)
  ↓ solve  P_hat(s*) = P0  (Photo: P0=0.003, Precision: P0=0.001)
  ↓ adaptive backoff if budget exceeded
  ↓ apply final sharpening(s*)  →  chroma guard
  ↓ clamp to [0,1]
  ↓ linear RGB → sRGB  →  save + recommendations

See docs/algorithm.md for a full pipeline description.


CLI reference

Flag Short Default Description
--input -i required Input image path
--output -o required Output image path
--width -W Target width (px)
--height -H Target height (px)
--preserve-aspect-ratio -p off Compute the missing dimension from the input aspect ratio
--target-artifact-ratio 0.003 P0 threshold (fraction, not percent)
--preset Named preset: photo (default), precision
--diagnostics Path to write a JSON diagnostics file
--diagnostics-level summary summary or full (per-probe breakdowns)
--probe-strengths two-pass Comma-separated explicit probe list
--sharpen-sigma 1.0 Gaussian sigma for unsharp mask
--sharpen-mode lightness lightness (CIE Y) or rgb
--metric-mode relative relative (sharpening-added) or absolute (total)
--artifact-metric channel-clipping channel-clipping or pixel-out-of-gamut
--metric-weights 1.0,0.3,0.3,0.1 Composite weights: gamut, halo, overshoot, texture
--selection-policy gamut-only gamut-only, hybrid, or composite-only
--enable-contrast-leveling off Enable contrast leveling stage (placeholder)
--sweep-dir Directory of images to process in batch mode
--sweep-output-dir Output directory for processed images (sweep mode)
--sweep-summary Path to write sweep summary JSON
--sweep-diff Compare two sweep summaries: BASE,CANDIDATE
--generate-corpus Generate synthetic benchmark corpus in directory

In single-file mode, --input and --output are required. Both --width and --height are required unless --preserve-aspect-ratio is set, in which case only one is needed.

In sweep mode, --sweep-dir replaces --input/--output. The sweep flags (--sweep-output-dir, --sweep-summary) require --sweep-dir.


Building and testing

# Build all crates
cargo build --workspace

# Run all tests
cargo test --workspace

# Lint (warnings are errors)
cargo clippy --workspace -- -D warnings

# Benchmarks
cargo bench -p r3sizer-core

TypeScript type generation

TypeScript types for the web UI are auto-generated from the Rust types in r3sizer-core using ts-rs. The generated file lives at web/src/types/generated.ts and should be regenerated whenever types in types.rs change:

cargo test -p r3sizer-core --features typegen export_typescript_bindings -- --nocapture

This also serializes Rust Default impls as TypeScript constants (DEFAULT_PARAMS, etc.) so the two sides never drift. The web app re-exports everything through web/src/types/wasm-types.ts, which adds WASM-specific types (ProcessResult) and any web-only overrides.

Web UI

The web UI is deployed to GitHub Pages at alvytsk.github.io/r3sizer via a GitHub Actions workflow (.github/workflows/deploy.yml). Every push to main triggers a build (Rust → WASM → Vite bundle) and deploys automatically.

For local development:

cd web

# Local development (requires WASM package to be built first)
npm run build:wasm
npm run dev

# Production build (builds WASM + TS check + Vite bundle)
npm run build

# Docker (from repo root)
docker build -f web/Dockerfile -t r3sizer-web .
docker run -p 8080:80 r3sizer-web

Library usage

r3sizer-core exposes the pipeline as a single function (one-shot) or as a two-phase API for interactive use:

use r3sizer_core::{AutoSharpParams, ProcessOutput, process_auto_sharp_downscale};

// One-shot (CLI / batch)
let params = AutoSharpParams::photo(800, 600);
let ProcessOutput { image, diagnostics } =
    process_auto_sharp_downscale(&input_linear_rgb, &params)?;

// Two-phase (interactive / WASM)
use r3sizer_core::{prepare_base, process_from_prepared};

let base = prepare_base(&input_linear_rgb, &params, &|_| {})?;
// ... user adjusts params (non-base-affecting) ...
let output = process_from_prepared(&base, &params, &|_| {})?;

AutoSharpDiagnostics is Serialize-able for JSON export and contains the full probe data, fit coefficients, selection mode, fit quality metrics (R², residuals), solver robustness flags (monotonicity, LOO stability), typed fallback reasons, per-stage timing, composite metric breakdowns, region coverage, chroma guard statistics, evaluator results, and parameter recommendations.


Documentation


License

MIT — see LICENSE.

About

Rust resizer library based on c3c algorithms

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors